请向我解释为什么我一直得到这个错误:ExpressionChangedAfterItHasBeenCheckedError:表达式已经改变后,它被检查。

显然,我只有在开发模式下才会遇到这种情况,在我的产品构建中不会出现这种情况,但这非常烦人,而且我根本不明白在我的开发环境中出现错误而不会在prod上显示的好处——可能是因为我缺乏理解。

通常,修复很简单,我只是把导致错误的代码包装在setTimeout中,就像这样:

setTimeout(()=> {
    this.isLoading = true;
}, 0);

或者使用如下构造函数强制检测更改:

this.isLoading = true;
this.cd.detectChanges();

但是为什么我总是遇到这个错误呢?我想要了解它,这样我就可以在将来避免这些俗套的修复。


当前回答

我得到这个错误,因为我在模式中调度redux动作,当时模式还没有打开。我正在调度动作的瞬间模态组件接收输入。所以我把setTimeout放在那里,以确保模式是打开的,然后动作被分派。

其他回答

如果你在ngAfterViewInit()中触发EventEmitter时看到ExpressionChangedAfterItHasBeenCheckedError,那么你可以在创建EventEmitter时传递true,使其在当前更改检测周期后异步发射。

@Component({
  ...
})
export class MyComponent implements AfterViewInit {
  // Emit asynchronously to avoid ExpressionChangedAfterItHasBeenCheckedError
  @Output() valueChange = new EventEmitter<number>(true);

  ...

  ngAfterViewInit(): void {
    ...
    this.valueChange.emit(newValue);
    ...
  }

}

此错误表明应用程序中存在真正的问题,因此抛出异常是有意义的。

在devMode中,更改检测在每次常规的更改检测运行之后增加一个额外的回合,以检查模型是否已更改。

如果模型在常规变化检测轮和附加变化检测轮之间发生了变化,则这表明也发生了变化

变更检测本身导致了变更 每次调用方法或getter都会返回不同的值

这两个都不好,因为不清楚如何继续,因为模型可能永远不会稳定。

如果Angular一直运行变更检测直到模型稳定,那么它可能会一直运行下去。 如果Angular不运行变更检测,那么视图可能不会反映模型的当前状态。

参见Angular2中生产模式和开发模式的区别是什么?

我得到这个错误,因为我在模式中调度redux动作,当时模式还没有打开。我正在调度动作的瞬间模态组件接收输入。所以我把setTimeout放在那里,以确保模式是打开的,然后动作被分派。

对于我的问题,我正在阅读github -“在afterViewInit中更改组件“非模型”值时的ExpressionChangedAfterItHasBeenCheckedError”,并决定添加ngModel

<input type="hidden" ngModel #clientName />

它解决了我的问题,我希望它能帮助到别人。

我用rxjs解决了这个问题

import { startWith, tap, delay } from 'rxjs/operators';

// Data field used to populate on the html
dataSource: any;

....

ngAfterViewInit() {
  this.yourAsyncData.
      .pipe(
          startWith(null),
          delay(0),
          tap((res) => this.dataSource = res)
      ).subscribe();
}