我有一个场景,其中两个组件彼此并排放置,分别是ComponentA和ComponentB。他们俩都使用服务Service。
ComponentB包含一个按钮,该按钮将使Service中的属性Service.counter上移1。
ComponentA呈现Service.counter的值。
但是,当我使用ChangeDetectionStrategy.OnPush时,无论尝试什么,我都无法获取ComponentA中的值进行更新,即使是从尝试此操作的根组件中也是如此:
this.cdr.markForCheck();
this.cdr.detectChanges();
this.ar.tick();
this.zone.run(() => {});
无需更改ComponentA,如何确保它始终显示正确的值?
(现实情况是,有很多组件(例如ComponentA)都呈现翻译后的值,并且当所选语言发生更改时,我需要所有这些翻译后的值进行相应的更新。单个组件,然后从那里调用detectChanges)
答案 0 :(得分:1)
但是,当我使用ChangeDetectionStrategy.OnPush时,无论尝试什么,我都无法获取ComponentA中的值进行更新,即使是从尝试此操作的根组件中也是如此:
组件具有关联的视图。该视图引用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()
方法返回的observable在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
的引用也会将视图标记为对您不利。因此,它省去了很多样板代码。
现实情况是,有很多组件(例如ComponentA)都呈现翻译后的值,并且当所选语言发生更改时,我需要所有这些翻译后的值进行相应的更新。我不想在每个单独的组件中构建一个侦听器,并从那里调用detectChanges
那么最好的选择是使用async
管道并使组件具有反应性。
如果我们要讨论的是大规模的事物并且影响很多组件,那么这个 root 组件应该将值作为@Input()
传递给组件,这也会触发它们被渲染。虽然这会在所有组件之间建立耦合,但您不必担心更新视图。