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

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

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

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

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

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

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


当前回答

在@ViewChild装饰器中添加static: true

@ViewChild(UploadComponent, { static: true }) uploadViewChild: UploadComponent;

其他回答

我在Ionic3(它使用Angular 4作为其技术堆栈的一部分)中遇到过这种错误。

对我来说,它是这样的:

< ion-icon[名字]= " getFavIconName ()" > < / ion-icon >

所以我试着有条件地改变离子图标的类型,从大头针到删除圈,根据屏幕运行的模式。

我猜我将不得不添加一个*ngIf代替。

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

我必须把ngIf改成[hidden]

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

to

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

在我的情况下,我在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>

当我添加*ngIf时,我的问题很明显,但这不是原因。该错误是由于在{{}}标签中更改模型,然后稍后尝试在*ngIf语句中显示更改后的模型而导致的。这里有一个例子:

<div>{{changeMyModelValue()}}</div> <!--don't do this!  or you could get error: ExpressionChangedAfterItHasBeenCheckedError-->
....
<div *ngIf="true">{{myModel.value}}</div>

为了解决这个问题,我将调用changeMyModelValue()的位置更改为更有意义的位置。

在我的情况下,我希望每当子组件更改数据时调用changeMyModelValue()。这要求我在子组件中创建并发出一个事件,以便父组件可以处理它(通过调用changeMyModelValue())。看到https://angular.io/guide/component-interaction parent-listens-for-child-event

当值在同一个更改检测周期中更改多次时,将发生此错误。我在一个TypeScript getter中遇到了这个问题,它的返回值经常变化。要解决这个问题,您可以限制一个值,使其在每个更改检测周期中只能更改一次,如下所示:

import { v4 as uuid } from 'uuid'

private changeDetectionUuid: string
private prevChangeDetectionUuid: string
private value: Date

get frequentlyChangingValue(): any {
  if (this.changeDetectionUuid !== this.prevChangeDetectionUuid) {
    this.prevChangeDetectionUuid = this.changeDetectionUuid
    this.value = new Date()
  }
  return this.value
}

ngAfterContentChecked() {
  this.changeDetectionUuid = uuid()
}

HTML:

<div>{{ frequentlyChangingValue }}</div>

这里的基本方法是每个变更检测周期都有自己的uuid。当uuid改变时,您就知道您已经进入了下一个循环。如果循环已经改变,则更新值并返回它,否则只需返回与之前在此循环中返回的值相同的值。

这确保每个循环只返回一个值。这对于频繁更新值很有效,因为更改检测周期发生得非常频繁。

为了生成uuid,我使用了uuid npm模块,但你可以使用任何方法来生成唯一的随机uuid。