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

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

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

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

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

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

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


当前回答

请遵循以下步骤:

1. 通过从@angular/core导入'ChangeDetectorRef'来使用它,如下所示:

import{ ChangeDetectorRef } from '@angular/core';

2. 在constructor()中实现,如下所示:

constructor(   private cdRef : ChangeDetectorRef  ) {}

3. 添加下面的方法到你的函数,你正在调用的事件,如点击按钮。它看起来是这样的:

functionName() {   
    yourCode;  
    //add this line to get rid of the error  
    this.cdRef.detectChanges();     
}

其他回答

在我的例子中,我在运行测试时在规范文件中遇到了这个问题。

我必须把ngIf改成[hidden]

<app-loading *ngIf="isLoading"></app-loading>

to

<app-loading [hidden]="!isLoading"></app-loading>

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

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

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

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

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

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

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

我用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();
}

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

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

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

更新

我强烈建议首先从OP的自我响应开始:适当地考虑在构造函数中可以做什么,而在ngOnChanges()中应该做什么。

原始

这与其说是一个答案,不如说是一个题外话,但它可能会帮助到一些人。当我试图让按钮的存在取决于表单的状态时,我偶然发现了这个问题:

<button *ngIf="form.pristine">Yo</button>

据我所知,这种语法会导致根据条件从DOM中添加和删除按钮。这又会导致ExpressionChangedAfterItHasBeenCheckedError。

在我的案例中,解决方案(尽管我没有声称掌握了差异的全部含义)是使用display: none来代替:

<button [style.display]="form.pristine ? 'inline' : 'none'">Yo</button>