我有一个场景,我有两个组件并排坐在一起,ComponentA和ComponentB。他们都使用服务服务。
ComponentB包含一个按钮,它将使Service,Service.counter中的属性增加1。
ComponentA呈现Service.counter的值。
但是,当我使用ChangeDetectionStrategy.OnPush时,无论我尝试什么,我都无法在ComponentA中获取更新的值,即使是从root组件,我尝试了这个:
this.cdr.markForCheck();
this.cdr.detectChanges();
this.ar.tick();
this.zone.run(() => {});
无需更改ComponentA,如何确保它始终显示正确的值?
(现实世界的情况是,像ComponentA这样的很多组件都呈现翻译的值,当所选语言发生变化时,我需要所有这些翻译的值进行相应的更新。我不想在每个单独的组件中构建一个监听器从那里调用detectChanges)
但是,当我使用ChangeDetectionStrategy.OnPush时,无论我尝试什么,我都无法在ComponentA中获取更新的值,即使是从root组件,我尝试了这个:
组件具有关联的视图。视图引用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()
方法返回一个在Angular范围之外执行的observable,并且你必须明确地这样做,因为异步操作被自动分区,那么你必须使用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
的引用也会将视图标记为脏。因此它可以节省大量的样板代码。
现实世界的情况是,像ComponentA这样的很多组件都呈现翻译的值,当所选语言发生变化时,我需要相应地更新所有这些翻译的值。我不想在每个组件中构建一个监听器,并从那里调用detectChanges
那么你最好的选择是使用async
管道,使你的组件反应。
如果我们正在谈论大规模的事情并影响很多组件,那么这个根组件可能会将值作为@Input()
传递给组件,这也会触发它们被渲染。虽然这会在所有组件之间创建耦合,但是您不必担心更新视图。