ExpressionChangedAfterItHasBeenCheckedError : 当事件从子组件发出时

Sam*_*uel 3 angular

这是 stackblitz 代码,当使用该service.ts文件进行 API 调用时,我从子组件发出事件。

我的孩子.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);
      })
  }


}
Run Code Online (Sandbox Code Playgroud)

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

如您所见,添加this.crd.detectChanges();通过告知angular这是需要另一个更改检测的部分来停止错误。

但是,我在 SO遇到了这个答案,其中这个错误基本上代表了BAD设计。并且使用detectChanges()似乎更像是一种修复,因为我必须明确地编写它。

有人可以帮助我进行GOOD设计,我不必detectChanges()在值将在生命周期中发生变化的地方明确地编写它。

Sha*_*vek 5

因此,我调查了实施您所写内容的最佳方法应该是什么。我向 Max 提出了同样的问题。我能得出的结论是

Angular 遵循单向数据流,你可以在这里阅读

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

  1. 使用this.crd.detectChanges()让角知道明确您已经更改了该值,所以角将是一种足以再次运行CD并不会引发任何错误

  2. 或者,show在孩子本身中实现行为,而不是违背 Parent -> Child 的流程。