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

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

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

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

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

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

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


当前回答

当值在同一个更改检测周期中更改多次时,将发生此错误。我在一个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。

其他回答

以下是我对正在发生的事情的看法。我还没有阅读文档,但我确信这是错误显示的部分原因。

*ngIf="isProcessing()" 

当使用*ngIf时,它会在每次条件改变时通过添加或删除元素来物理地改变DOM。因此,如果条件在呈现给视图之前发生了变化(这在Angular的世界中是极有可能的),就会抛出错误。请参阅开发和生产模式之间的解释。

[hidden]="isProcessing()"

当使用[hidden]时,它不会在物理上改变DOM,而只是从视图中隐藏元素,很可能在后面使用CSS。元素仍然在DOM中,但不可见,这取决于条件的值。这就是为什么使用[hidden]时不会出现错误的原因。

当值在同一个更改检测周期中更改多次时,将发生此错误。我在一个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。

我也遇到过类似的问题。查看生命周期钩子文档,我将ngAfterViewInit更改为ngAfterContentInit,它工作正常。

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

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

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

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