为啥 ngOnChange 没有检测到 @Input 元素更改而 ngOnDetect 能够这样做

Posted

技术标签:

【中文标题】为啥 ngOnChange 没有检测到 @Input 元素更改而 ngOnDetect 能够这样做【英文标题】:Why is ngOnChange not detecting @Input element change whil ngOnDetect was able to do so为什么 ngOnChange 没有检测到 @Input 元素更改而 ngOnDetect 能够这样做 【发布时间】:2016-08-08 20:11:09 【问题描述】:

考虑一下plunker

注意:进入链接后需要重新运行app才能观察效果

import Component, OnInit, Input, OnChanges, DoCheck from 'angular2/core'

@Component(
  selector: 'sub',
  template: `
    <li  *ngFor="#elem of sub_list">
      <div>elem['name']</div>
    </li>
    `
)
export class Sub 

  @Input()
  sub_list: Object[];

  ngOnInit()
    console.log('init');
    console.log(this.sub_list);
   

  ngOnChanges()
    console.log('change');
    console.log(this.sub_list);
  

  ngDoCheck() 
    console.log('check');
    console.log(this.sub_list);
  


@Component(
  selector: 'my-app',
  template: `
    <div>
      <sub
        [sub_list]="my_list"
      >
      </sub>

    </div>

    `,
  directives: [Sub]
)
export class App 

  my_list: Object[] = [];

  ngOnInit() 
      var vm = this;

    setTimeout(function() 
          for(var i = 0; i < 100; i++) 

        vm.my_list.push(
          'index' : i,
          'name'  : i
        );
      
    , 100);

  

如果我尝试在SubngOnChange 中打印出this.sub_list,浏览器会输出一个空列表。

但是我们可以看到ngDoCheck 仍然正确地捕获了更改。

这有什么具体原因吗?

【问题讨论】:

【参考方案1】:

好的,我知道了。 ngOnChanges 在您使用空数组初始化类字段时触发,然后您在超时回调中更新数组,并且由于 Thierry 正确指出的更改未被检测到。

Angular 包含 zone.js 库,可以跟踪应用程序中的所有异步事件。 Zone 检测到超时回调在某处执行并触发ngDoCheck 循环,您将获得正确的日志。

见plunkr

【讨论】:

如果你注释掉DoCheck,result 是一样的。我希望OnChanges 能够捕捉到模型变化【参考方案2】:

在您的情况下,不会调用 ngOnChanges 来更新阵列。事实上,Angular2 根据引用检测更新。我的意思是如果整个数组的引用没有改变(使用 push 方法在其中添加元素时就是这种情况),则不会调用 ngOnChanges 方法。

在您的情况下,当调用 ngOnChanges 时,您的数组为 null,因为它是在设置输入元素之前调用的。

在这种情况下有两种检测变化的方法:

使用 slice(推送后)或 concat 等方法更新整个数组引用。

this.myArray.push(...);
this.myArray.push = this.myArray.push.slice();

利用 ngDoCheck 方法和 IterableDiffers 类来手动检查更新。该类允许您注册回调,以便在数组中添加(或删除)元素时收到通知。

查看这些链接了解更多详情:

Angular2 change detection: ngOnChanges not firing for nested object https://angular.io/docs/ts/latest/api/core/DoCheck-interface.html

【讨论】:

嗯,这很令人困惑,如果Angular2 总是检查引用相等,那么changeDetection: ChangeDetectionStrategy.OnPush 的意义何在 OnPush 是一种告诉 angular2 不要检查特定组件子树的方法。例如对于不可变对象很有用。在所有情况下,“更改”默认对应于一个新实例。利用 ngDoCheck 是一种提供自定义方式的方式。 查看此链接了解更多详情:blog.thoughtram.io/angular/2016/02/22/….

以上是关于为啥 ngOnChange 没有检测到 @Input 元素更改而 ngOnDetect 能够这样做的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Angular 2 ngOnChanges 不响应输入数组推送

ngOnChanges无法检测数组内容修改时解决方案

Angular ngOnChanges 和变化检测策略似乎是矛盾的?

IONIC2/Angular2 ngOnChange 不起作用

尽管我们在父组件中更改了两次属性的值,但 ngOnChanges 没有被调用两次

如何停止在 ngOnInit() 之前调用的 ngOnChanges