双向Angular绑定中的ExpressionChangedAfterItHasBeenCheckedError

时间:2017-09-06 01:37:49

标签: angular typescript angular2-changedetection

这是example

@Component({
  selector: 'my-app',
  template: `
    <div>
      <h1>{{ foo }}</h1>    
      <bpp [(foo)]="foo"></bpp>
    </div>
  `,
})
export class App {
  foo;
}

@Component({
  selector: 'bpp',
  template: `
    <div>
      <h2>{{ foo }}</h2>
    </div>
  `,
})
export class Bpp {
  @Input('foo') foo;

  @Output('fooChange') fooChange = new EventEmitter();

  ngAfterViewInit() {
    const potentiallyButNotNecessarilyAsyncObservable = Observable.of(null);

    potentiallyButNotNecessarilyAsyncObservable.subscribe(() => {
      this.fooChange.emit('foo');
    })
  }
}

偶尔会出现错误:

  

ExpressionChangedAfterItHasBeenCheckedError:表达式在检查后发生了变化。以前的值:&#39; undefined&#39;。当前价值:&#39; foo&#39;

这是因为双向绑定由一个可以获得相同tick上的值的observable更新。我宁愿不用setTimeout包装上面的逻辑,因为它看起来像一个黑客并使控制流复杂化

可以采取哪些措施来避免此错误?

ExpressionChangedAfterItHasBeenCheckedError错误会产生不良影响还是可以忽略?如果可以的话,可以改变探测器是否静音而不污染控制台?

1 个答案:

答案 0 :(得分:6)

让我们首先解开双向数据绑定以简化说明:

<div>
  <h1>{{ foo }}</h1>    
  <bpp [foo]="foo" (fooChange)="foo=$event"></bpp>
</div>

它仍然具有相同的效果,偶尔会产生错误。只有potentiallyButNotNecessarilyAsyncObservable是同步的,才会产生错误。所以我们也可以替换它:

ngAfterViewInit() {
    const potentiallyButNotNecessarilyAsyncObservable = Observable.of(null);

    potentiallyButNotNecessarilyAsyncObservable.subscribe(() => {
      this.fooChange.emit('foo');
    })

用这个:

ngAfterViewInit() {
    this.fooChange.emit('foo');

此案例属于文章Everything you need to know about the ExpressionChangedAfterItHasBeenCheckedError error中解释的Synchronous event broadcasting类错误。

在处理父组件更改后触发ngAfterViewInit生命周期挂钩。 Everything you need to know about change detection in Angular中解释了与子组件相关的挂钩顺序。现在Angular记得当它运行App组件的更改检测时,foo的值为undefined,但在验证阶段,值为foo,由孩子更新Bpp组件。因此它会产生错误。

  

可以采取哪些措施来避免此错误?

修复和问题在我链接的文章中描述。如果您不想重新设计逻辑,那么唯一安全的选择是异步更新。您还可以对父组件运行更改检测,但它们可能会导致无限循环,因为组件上的更改检测会触发对组件子项的更改检测。

  

ExpressionChangedAfterItHasBeenCheckedError错误是否有效   效果还是可以忽略?

不良影响是,您将在应用程序App.foo==='foo'和视图{{foo}}===undefined中具有不稳定状态,直到下一次digest cycle次迭代。在开发模式下无法关闭错误,但它不会出现在生产模式中。

Two Phases of Angular Applications在解释此错误的心理模型方面也非常出色。