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

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

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

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

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

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

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


当前回答

我希望这能帮助到在座的各位: 我们在ngOnInit中以以下方式调用服务,并使用一个变量displayMain来控制元素到DOM的挂载。

component.ts

  displayMain: boolean;
  ngOnInit() {
    this.displayMain = false;
    // Service Calls go here
    // Service Call 1
    // Service Call 2
    // ...
    this.displayMain = true;
  }

和component.html

<div *ngIf="displayMain"> <!-- This is the Root Element -->
 <!-- All the HTML Goes here -->
</div>

其他回答

如果你在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);
    ...
  }

}

当我添加*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

参考文章https://blog.angularindepth.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4

因此,变更检测背后的机制实际上是以同步执行变更检测和验证摘要的方式工作的。这意味着,如果我们异步更新属性,当验证循环运行时,值不会被更新,我们也不会得到ExpressionChanged…错误。我们得到这个错误的原因是,在验证过程中,Angular看到的值与它在变更检测阶段记录的值不同。所以为了避免....

1)使用changeDetectorRef

2)使用setTimeOut。这将在另一个VM中作为宏任务执行您的代码。Angular在验证过程中不会看到这些更改,你也不会得到这个错误。

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

3)如果你真的想在相同的虚拟机上执行你的代码使用

Promise.resolve(null).then(() => this.isLoading = true);

这将创建一个微任务。微任务队列是在当前同步代码完成执行之后处理的,因此对属性的更新将在验证步骤之后发生。

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

Angular会运行更改检测,当它发现传递给子组件的某些值被更改时,Angular会抛出以下错误:

点击查看更多信息

为了纠正这个问题,我们可以使用AfterContentChecked生命周期钩子和

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

  constructor(
  private cdref: ChangeDetectorRef) { }

  ngAfterContentChecked() {

    this.cdref.detectChanges();

  }