【问题标题】:Angular OnPush: how do I force changes to be detected from outside?Angular OnPush:如何强制从外部检测更改?
【发布时间】:2019-09-18 04:02:01
【问题描述】:

我有一个场景,我有两个组件并排放置,即 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)

【问题讨论】:

  • 如果您不想在每个组件中构建一个监听器,您是否可以编写一个父类来监听来自服务的更改并让您的所有ComponentBs 扩展它?如果没有,您可以对这些组件使用inputs,这将触发 onPush 更改。

标签: angular angular2-changedetection


【解决方案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() 方法返回一个在 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() 传递给组件,这也会触发它们呈现。虽然这会在所有组件之间建立耦合,但它表明您不必担心更新视图。

【讨论】:

  • 我考虑过为此使用管道(事实上,我正在使用 @ngx-translate ,其中 |translate 似乎正在执行您的建议),但出于某种原因,即使是这个我无法上班。在这里查看这个小项目wetransfer.com/downloads/…。 Cmp1 尝试使用管道呈现来自服务的值,该管道在值更改时调用 markForCheck,但在视图中不会更改。我在这里做错了什么?
  • 我刚刚意识到我的示例不起作用,因为我没有在管道上将pure 设置为false,所以在更改后它确实有效!现在要弄清楚为什么它在我的项目中不起作用...
  • 对我来说,您的 wetransfer 链接不起作用。例如,在此处显示相关代码部分或将您的代码签入 GitHub 始终是最好的主意。
猜你喜欢
  • 2017-09-28
  • 2017-08-08
  • 1970-01-01
  • 1970-01-01
  • 2020-10-04
  • 2013-04-26
  • 2021-03-06
  • 2019-07-01
  • 2021-11-10
相关资源
最近更新 更多