What's going on?
Angular has a LifeCycle Management according to which it will update the a components class and view.
It starts with ngOnChanges
, then progresses to ngOnInit
(which only runs once), and then progresses to ngAfterViewInit
(also only running once). I've not listed all lifecycle hooks here, to see the full list check
https://angular.io/guide/lifecycle-hooks.
Now what Angular doesn't like is if you mess with class properties you're also referencing in the view (= the html) inside of ngAfterViewInit
or ngAfterViewChecked
. The reason for this is that Angular has already set up the html and doesn't want to check everything again. If you're doing that, Angular will throw a ExpressionChangedAfterItHasBeenCheckedError
error and will not update the view for you. So what can you do to mitigate the problem?
Use the ChangeDetectorRef
If you're using an ngAfterViewInit
, you can try to solve the problem by letting Angular know it needs to check for changes again. Just add the following code to your class:
import { Component, OnInit, AfterViewInit, ViewChild, ElementRef, ChangeDetectorRef } from '@angular/core';
@Component({
selector: 'my-app',
template: ``
})
export class AppComponent {
someProperty = 0;
constructor(
private cdr: ChangeDetectorRef
) {}
ngAfterViewInit() {
this.someProperty = 1;
this.cdr.detectChanges();
}
}
You can find this code on Stackblitz here:
https://stackblitz.com/edit/angular-use-cdr-to-mitigate-expressionhaschangedafterchecked?file=src/app/app.component.ts
Find the problematic "expression" aka class property
Often you'll know by looking at the error message which property Angular is talking about. For example if Angular says:
ERROR
Error: ExpressionChangedAfterItHasBeenCheckedError:
Expression has changed after it was checked. Previous value: '5'. Current value: '6'.
You'll know, "aha, it was the property I initialized with the value 5" and you'll know which one this is.
If you don't know immediately which property Angular is talking about you can try the following things to find it:
-
Check the error message for a location in the code, e.g.:
app.component.html:3
. Check which properties are being referenced on that line. -
Search for properties being changed in
ngAfterViewInit
orngAfterViewChanged
. -
Divide and conquer: Comment out parts of your
ngAfterViewChanged
method. -
Change all the initial values to something distinct you can identify. For example at the end of your
ngOnInit
, change all the properties of your class to something else e.g.width = 1; height = 2; ...
so you can identify the problematic property with the help of the error message.
Those methods should help you to isolate and track down the problematic expression.
Conclusion
An error message identifier with 43 characters might scare you first. But after understanding the lifecycle methods, you also understand this error better and that it's actually not that hard to debug.