Angular onPush不会从父级更新子属性

时间:2018-04-16 19:57:34

标签: javascript angular

我有一个看起来像这样的子组件:

@Component({
  selector: 'app-child',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    {{text}}
  `
})
export class ChildComponent {
  @Input() text = '';

  constructor(public host: ElementRef) { }
}

一个看起来像这样的父组件:

@Component({
  selector: 'app-parent',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `<ng-content></ng-content>`
})
export class ParentComponent {
  @ContentChild(ChildComponent) child: ChildComponent;

  constructor(private cdr: ChangeDetectorRef) { }

  ngAfterContentInit() {
    this.child.text = 'hello';
    this.child.host.nativeElement.addEventListener('click', () => {
      this.child.text = 'from click';
      this.cdr.detectChanges();
    });
  }

第一次分配给text属性工作正常,但是当我单击按钮并尝试再次更改text属性时,没有任何事情发生。

由于我的了解,这令人困惑: 1. click事件应触发更改检测,text属性不同,因此应该更新。 2.我明确地打电话给detectChanges(),这也应该从我所知道的那里检查孩子们。

我错过了什么?

1 个答案:

答案 0 :(得分:4)

问题与GitHub上报告的this issue有关。它发生在:

  • OnPush更改检测策略用于子组件
  • 子组件的输入属性直接在父组件代码中更改,而不是在父组件模板中进行数据绑定

AngularInDepth.com给出的解释:

  

编译器没有办法生成检查所需的信息   绑定因为它无法在模板中找到这些绑定。   OnPush与输入绑定紧密绑定。重要的是什么   Angular检查绑定的第二部分(prop中的prop)   例子如下),而不是第一个(i):

<child [i]="prop">
     

确定是否应该为孩子运行更改检测   零件。在检查父组件时它会这样做。如果你不这样做   向编译器显示应该使用哪个父属性来更新子级   输入绑定,它不能生成必要的信息时使用   检查父母。因此检查子组件上的@Input是不是   足够。这是变化检测的机制,我什么都看不到   它可以改变的方式。

yurzui在讨论中建议的一种解决方法是在设置ChangeDetectorRef.markForCheck属性后调用子组件中的text,如this stackblitz所示。事实上,它可以在不调用父组件中的ChangeDetectorRef.detectChanges的情况下工作。

export class ChildComponent {

  private _text = '';

  @Input()
  get text() {
    return this._text;
  }
  set text(val) {
    if (this._text !== val) {
      this.cdRef.markForCheck();
    }
    this._text = val;
  }

  constructor(public host: ElementRef, private cdRef: ChangeDetectorRef) { }
}