为什么 ngOnChange 没有检测到 @Input 元素更改,而 ngOnDetect 却能够做到

tes*_*ing 5 javascript typescript angular

考虑一下这个笨蛋

注意:要观察效果,​​您必须在输入链接后重新运行应用程序

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);

  }
}
Run Code Online (Sandbox Code Playgroud)

如果我尝试打印this.sub_listin Sub,浏览ngOnChange器会输出一个空列表。

然而我们可以看到ngDoCheck仍然正确地捕获了变化。

这有什么具体原因吗?

Thi*_*ier 5

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

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

在这种情况下,有两种方法可以检测变化:

  • 使用切片(推送后)或连接等方法更新整个数组引用。

    this.myArray.push({...});
    this.myArray.push = this.myArray.push.slice();
    
    Run Code Online (Sandbox Code Playgroud)
  • 利用 ngDoCheck 方法和 IterableDiffers 类来手动检查更新。该类允许您注册回调,以便在数组中添加(或删除)元素时收到通知。

请参阅这些链接了解更多详细信息:

  • OnPush 是一种告诉 Angular2 不要检查特定组件子树的方法。例如,它对于不可变对象很有用。在所有情况下,“更改”默认对应于新实例。利用 ngDoCheck 是提供自定义方式的方法。 (2认同)