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

und*_*ned 8 javascript angular

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

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

  constructor(public host: ElementRef) { }
}
Run Code Online (Sandbox Code Playgroud)

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

@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();
    });
  }
Run Code Online (Sandbox Code Playgroud)

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

这很令人困惑,因为据我所知: 1. click 事件应该触发更改检测,并且 text 属性不同,因此应该已更新。2.我明确地打电话detectChanges(),这也应该检查我所知道的孩子们。

我错过了什么?

Con*_*Fan 10

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

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

AngularInDepth.com给出的解释:

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

<child [i]="prop">
Run Code Online (Sandbox Code Playgroud)

以确定是否应为子组件运行更改检测。它在检查父组件时这样做。如果您不向编译器显示应该使用哪个父属性来更新子输入绑定,则它无法生成检查父项时使用的必要信息。所以在子组件上检查 @Input 是不够的。这就是变化检测的机制,我看不出有任何可以改变的方式。

yurzui在讨论中建议的一种解决方法是ChangeDetectorRef.markForCheck在设置text属性后调用子组件,如此 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) { }
}
Run Code Online (Sandbox Code Playgroud)