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

时间:2019-04-29 13:55:34

标签: angular angular2-changedetection

我有一个场景,其中两个组件彼此并排放置,分别是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)

1 个答案:

答案 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()传递给组件,这也会触发它们被渲染。虽然这会在所有组件之间建立耦合,但您不必担心更新视图。