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

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

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

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

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

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

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


当前回答

调试技巧

这个错误可能非常令人困惑,而且很容易对它发生的确切时间做出错误的假设。我发现在受影响的组件的适当位置添加大量这样的调试语句很有帮助。这有助于理解流程。

在父类的put语句中(确切的字符串'EXPRESSIONCHANGED'很重要),但除此之外,这些只是例子:

    console.log('EXPRESSIONCHANGED - HomePageComponent: constructor');
    console.log('EXPRESSIONCHANGED - HomePageComponent: setting config', newConfig);
    console.log('EXPRESSIONCHANGED - HomePageComponent: setting config ok');
    console.log('EXPRESSIONCHANGED - HomePageComponent: running detectchanges');

在子/ services / timer回调函数中:

    console.log('EXPRESSIONCHANGED - ChildComponent: setting config');
    console.log('EXPRESSIONCHANGED - ChildComponent: setting config ok');

如果你手动运行detectChanges,添加日志记录:

    console.log('EXPRESSIONCHANGED - ChildComponent: running detectchanges');
    this.cdr.detectChanges();

然后在Chrome调试器只是过滤'EXPRESSIONCHANGES'。这将显示所有设置的流程和顺序,以及Angular抛出错误的确切位置。

您还可以单击灰色链接来放入断点。

另一件需要注意的事情是,如果你在整个应用程序中都有类似命名的属性(比如style.background),请确保你正在调试你认为你在调试的属性——通过将它设置为一个模糊的颜色值。

其他回答

我的问题是我在加载这个对象时打开了一个Ngbmodal弹出框,这个对象在被检查后被改变了。我能够通过打开setTimeout内的模态弹出框来解决它。

setTimeout(() => {
  this.modalReference = this.modalService.open(this.modal, { size: "lg" });
});

我得到这个错误,因为我在component.html中使用了一个没有在component.ts中声明的变量。一旦我删除了HTML中的部分,这个错误就消失了。

在我的情况下,我在LoadingService中有一个行为主题是loading的异步属性

使用[hidden]模型可以工作,但是*ngIf失败

    <h1 [hidden]="!(loaderService.isLoading | async)">
        THIS WORKS FINE
        (Loading Data)
    </h1>

    <h1 *ngIf="!(loaderService.isLoading | async)">
        THIS THROWS ERROR
        (Loading Data)
    </h1>

调试技巧

这个错误可能非常令人困惑,而且很容易对它发生的确切时间做出错误的假设。我发现在受影响的组件的适当位置添加大量这样的调试语句很有帮助。这有助于理解流程。

在父类的put语句中(确切的字符串'EXPRESSIONCHANGED'很重要),但除此之外,这些只是例子:

    console.log('EXPRESSIONCHANGED - HomePageComponent: constructor');
    console.log('EXPRESSIONCHANGED - HomePageComponent: setting config', newConfig);
    console.log('EXPRESSIONCHANGED - HomePageComponent: setting config ok');
    console.log('EXPRESSIONCHANGED - HomePageComponent: running detectchanges');

在子/ services / timer回调函数中:

    console.log('EXPRESSIONCHANGED - ChildComponent: setting config');
    console.log('EXPRESSIONCHANGED - ChildComponent: setting config ok');

如果你手动运行detectChanges,添加日志记录:

    console.log('EXPRESSIONCHANGED - ChildComponent: running detectchanges');
    this.cdr.detectChanges();

然后在Chrome调试器只是过滤'EXPRESSIONCHANGES'。这将显示所有设置的流程和顺序,以及Angular抛出错误的确切位置。

您还可以单击灰色链接来放入断点。

另一件需要注意的事情是,如果你在整个应用程序中都有类似命名的属性(比如style.background),请确保你正在调试你认为你在调试的属性——通过将它设置为一个模糊的颜色值。

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

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

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