Angular OnPush:如何强制从外部检测更改?

Angular OnPush: how do I force changes to be detected from outside?

我有一个场景,我有两个组件并排放置,ComponentA 和 ComponentB。他们都使用一个服务Service。

ComponentB 包含一个按钮,它将在服务中生成 属性,Service.counter,上升 1。

ComponentA 呈现 Service.counter 的值。

但是,当我使用 ChangeDetectionStrategy.OnPush 时,无论我尝试什么,我都无法获取要更新的 ComponentA 中的值,即使是从我试过的根组件也是如此:

this.cdr.markForCheck();
this.cdr.detectChanges();
this.ar.tick();
this.zone.run(() => {});

无需更改 ComponentA,如何确保它始终显示正确的值?

(现实世界的场景是有很多组件,例如Componenta所有渲染翻译值,当所选的语言更改时,我需要所有这些翻译值来相应地更新。我不想构建听众进入每个单独的组件并从那里调用 detectChanges)

However, when I use ChangeDetectionStrategy.OnPush, no matter what I try, I cannot get the value in ComponentA to update, even from the root component, where I tried this:

一个组件有一个关联的视图。该视图引用 DOM 并且是我们要更新的内容。当您使用 OnPush 时,如果组件的状态在外部发生变化,则需要将组件的视图标记为脏。

当您说 even from the root component 时,这意味着您正试图将错误的视图标记为脏视图。如果您想查看 ComponentA 中的更改,则需要将该组件视图标记为脏。

像这样。

@Component({...})
public class ComponentA implements OnInit {
    public count; // rendered in the view

    public constructor(private _change: ChangeDetectorRef,
                       private _service: MyService) {
    }

    public onInit() {
         this._service.getCounter().subscribe(value=> {
             this.count = value; // will not update the view.
             this._change.markForCheck(); // tell Angular it's dirty
         });
    }
}

所以上面的方法在 99% 的情况下都有效,但是如果 getCounter() 方法 returns 一个在 Angular 范围之外执行的可观察对象,你必须这样做这是明确的,因为异步操作是自动分区的,那么你必须使用 zone.run() 方法。否则,即使您将视图标记为脏。 Angular 不会检查是否有任何视图需要更新。除非您使用非 Angular 事件或在 Angular.

之外明确 运行 否则不应发生这种情况

替代方法是使用 async 管道,这是更简单的方法。

@Component({
    template: `<span>{{count$ | async}}</span>`
})
public class ComponentA implement OnInit {
    public count$: Observable<number>;

    public constructor(private _service: MyService) {
    }

    public onInit() {
        this.count$ = this._service.getCounter();
    }
}

async 管道使用对 ChangeDetectorRef 的引用也会将视图标记为脏视图。所以它可以让您免于编写大量样板代码。

The real world scenario is that there's a lot of components like ComponentA all rendering translated values, and when the selected language changes, I need all these translated values to update accordingly. I don't want to build a listener into each individual component and call detectChanges from there

那么你最好的选择是使用 async 管道并使你的组件具有反应性。

如果我们谈论的是大规模的东西并且影响很多组件,那么也许这个 root 组件应该将值作为 @Input() 传递给组件这也将触发它们被渲染。虽然这会在所有组件之间创建耦合,但它表明您不必担心更新视图。