ExpressionChangedAfterItHasBeenCheckedError:从子组件发出事件时

时间:2018-06-10 14:02:37

标签: angular

Here is the stackblitz code,当使用service.ts文件进行API调用时,我从子组件发出事件。

我的 child.comp.ts

import { Component, Output,EventEmitter } from '@angular/core';
import {Service} from './service';

@Component({
  selector: 'child',
  template: `Child data: {{data}}`
})
export class ChildComponent  {
  @Output() change = new EventEmitter();
  data: string;
  constructor(private svc: Service){}

  ngOnInit(){
    this.change.emit(true);
    this.svc.getMsg()
      .subscribe( data =>{
        this.data = data;
        this.change.emit(false);
      })
  }


}

我的 app.comp.ts

import { Component ,ChangeDetectorRef} from '@angular/core';
import {Service} from './service';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  name = 'Angular 6';
  show= false;
  constructor(svc: Service,private crd: ChangeDetectorRef){}
  showData(event){
    this.show = event;
    // this.crd.detectChanges(); // this stops the error
  }
}

正如您所看到的那样,通过让this.crd.detectChanges();知道这是需要进行其他更改检测的部分,添加angular可以阻止错误。

但是,I came across this answer on SO此错误基本上代表 BAD 设计。使用detectChanges()似乎更像是一个修复,因为我必须明确地写它。

有人可以帮助我完成 GOOD 设计,我不必在生命周期中值更改的地方明确地detectChanges()

2 个答案:

答案 0 :(得分:1)

所以,我调查了实现你所写内容的最佳方法。 I asked the same question to Max。我能得出的结论是

  

Angular遵循单向数据流as you can read over here

在您的情况下,您基本上是在CD中间更改show的值。现在发生的事情是,当在DEV模式下第二次触发CD时,它会发现您使用EventEmitter更改了来自子组件的父数据。这与 Angular的单向流相矛盾。在您的情况下可能的解决方案,

  1. 使用this.crd.detectChanges()让角度知道显式您已经更改了值,因此角度将足以再次运行CD并且不会抛出任何值误差

  2. 或者,在子项本身中实现show行为,而不是反对Parent的流程 - >子。

答案 1 :(得分:0)

当您以开发模式运行应用程序时(默认),更改检测器会遍历组件树两次:

  1. 确保您在生命周期挂钩中没有任何代码 可以导致UI更改(我猜,你的代码违反了这一点 从ngOnInit发射事件。
  2. 将UI与代码
  3. 同步

    那么问问自己是否真的需要从ngOnInit发出这个消息,这可能会导致其他地方的UI更改?如果你找不到更好的地方,尝试移动setTimeout()函数中的代码,这将确保在下一个事件循环周期调用此代码,当更改检测器完成其传递时