Angular 2中的ChangeDetectionStrategy.OnPush和Observable.subscribe

时间:2016-02-14 00:02:29

标签: javascript angular

ChangeDetectionStrategy.OnPush旁边使用Observable时,我试图围绕最佳做法。

该示例演示了想要显示某种加载消息(或者可能是简单的微调器动画)的常见场景:

Plnkr here

@Component({
  selector: 'my-app',
  template: `Are we loading?: {{loadingMessage}}`,

  // Obviously "Default" will notice the change in `loadingMessage`...
  // changeDetection: ChangeDetectionStrategy.Default

  // But what is best practice when using OnPush?
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class App implements OnInit {

  private loadingMessage = "Wait for it...";

  constructor() {

  }

  ngOnInit() {

    // Pretend we're loading data from a service.
    // This component is only interested in the call's success
    Observable.of(true)
      .delay(2000)
      .subscribe(success => {

        if(success){
          console.log('Pretend loading: success!');

          // OnPush won't detect this change
          this.loadingMessage = 'Success!';
        }

      });

  }
}

我或多或少地理解OnPush对不变性的要求,至少对我而言,当谈到实际模型数据(可能存在于某种商店中)时,它目前是有意义的。

所以,我有两个问题:

  1. 为什么新字符串值'Success!'的分配不会触发更改检测器?就不变性而言,价值已发生变化,对吗?
  2. 使用loadingMessage时,如何实现轻量级内部组件状态(即ChangeDetectionStrategy.OnPush)?如果有多种最佳实践,请指出正确的方向。

3 个答案:

答案 0 :(得分:44)

好问题。我有两个来自Savkin的关于onPush的引用(因为Angular.io文档似乎没有关于这个主题的任何信息):

  

只有在输入更改或组件模板发出事件时,框架才会检查OnPush组件。 - ref

     

使用OnPush时,Angular只会在其任何输入属性发生更改时,触发事件时或者当observable触发事件时检查该组件。 - ref(在对@vivainio的评论回复中)

第二句话似乎更完整。 (太糟糕了,它被埋没在评论中!)

  

为什么新字符串值Success!的赋值不会触发   改变探测器?就不变性而言,价值已发生变化,对吗?

OnPush不变性是指输入属性,而不是普通的实例属性。如果loadingMessage是输入属性并且值已更改,则将在组件上执行更改检测。 (参见@Vlado对Plunker的回答。)

  

使用loadingMessage时,如何实现轻量级内部组件状态(即ChangeDetectionStrategy.OnPush)?如果有多种最佳实践,请指出正确的方向。

这是我目前所知道的:

  • 如果我们将内部状态更改为处理事件或可观察触发的一部分,则更改检测将在OnPush组件上执行(即视图将更新)。在您的特定情况下,我很惊讶该视图没有更新。这里有两个猜测,为什么不:
    • 添加delay()使Angular更像是setTimeout()而不是可观察到的更改。 setTimeout不会导致OnPush组件上的更改检测执行。在您的示例中,更改检测器已在loadingMessage的值更改之前2秒完成其工作。
    • 就像@Sasxa在他的回答中所示,你必须在模板中绑定Observable。也许它不仅仅是“一个可观察到的火焰”......也许它必须是一个强烈的绑定可观察物。在这种情况下,创建loadingMessage(或甚至更通用的state属性)作为Observable本身将允许您将模板绑定到其值(或多个异步值),请参阅{ {3}}。
      更新2016-04-28:似乎绑定必须包含| async,如plnkr和this example plnkr所示。< / LI>
  • 如果我们更改内部状态并且它不是事件处理或可观察触发的一部分,我们可以将ChangeDetectorRef注入到我们的组件中并调用方法markForCheck()以使更改检测在{ {1}}组件和所有祖先组件直到根组件。如果仅改变视图状态(即,组件本地的状态以及其后代的状态),则可以使用OnPush代替,这不会标记用于变化检测的所有祖先组件。 this plnkr

答案 1 :(得分:6)

AFAIK,在directly with observables工作时使用OnPush

//our root app component
import {Component, OnInit, ChangeDetectionStrategy} from 'angular2/core'
import {Observable} from 'rxjs/Observable';
import 'rxjs/Rx';

@Component({
  selector: 'my-app',
  template: `Are we loading?: {{ loadingMessage |async }}`,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class App implements OnInit {
  private loadingMessage;
  constructor() { }

  ngOnInit() {
    this.loadingMessage = Observable.of(true)
      .delay(2000)
  }
}

答案 2 :(得分:1)

使用此ChangeDetectionStrategy.OnPush,您告诉您的组件只监听其输入属性的更改。

我在您的示例中添加了加载组件,以向您展示它是如何工作的: http://plnkr.co/edit/k5tkmcLlzkwz4t5ifqFD?p=preview