Please explain to me why I keep getting this error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.
Obviously,
A solution that worked for me using rxjs
import { startWith, tap, delay } from 'rxjs/operators';
// Data field used to populate on the html
dataSource: any;
....
ngAfterViewInit() {
this.yourAsyncData.
.pipe(
startWith(null),
delay(0),
tap((res) => this.dataSource = res)
).subscribe();
}
My issue was manifest when I added *ngIf
but that wasn't the cause. The error was caused by changing the model in {{}}
tags then trying to display the changed model in the *ngIf
statement later on. Here's an example:
<div>{{changeMyModelValue()}}</div> <!--don't do this! or you could get error: ExpressionChangedAfterItHasBeenCheckedError-->
....
<div *ngIf="true">{{myModel.value}}</div>
To fix the issue, I changed where I called changeMyModelValue()
to a place that made more sense.
In my situation I wanted changeMyModelValue()
called whenever a child component changed the data. This required I create and emit an event in the child component so the parent could handle it (by calling changeMyModelValue()
. see https://angular.io/guide/component-interaction#parent-listens-for-child-event
I was struggling with this issue for a while, primarily while running tests in my dev environment. I was able to resolve the issue with a simple setTimeout around a service call response!
Tried most of the solutions suggested above. Only this worked for me in this scenario. I was using *ngIf to toggle angular material's indeterminate progressive bar based on api calls and it was throwing ExpressionChangedAfterItHasBeenCheckedError
.
In the component in question:
constructor(
private ngZone: NgZone,
private changeDetectorRef: ChangeDetectorRef,
) {}
ngOnInit() {
this.ngZone.runOutsideAngular(() => {
this.appService.appLoader$.subscribe(value => {
this.loading = value;
this.changeDetectorRef.detectChanges();
});
});
}
The trick is to bypass angular component's change detection using ngzone.
PS: Not sure if this is an elegant solution but using AfterContentChecked and AfterViewChecked lifecycle hook is bound to raise performance issues as your application gets bigger as it is triggered numerous times.
This error can be quite confusing, and it's easy to make a wrong assumption about exactly when it's occuring. I find it helpful to add a lot of debugging statements like this throughout the affected components in the appropriate places. This helps understand the flow.
In the parent put statements like this (the exact string 'EXPRESSIONCHANGED' is important), but other than that these are just examples:
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');
In the child / services / timer callbacks:
console.log('EXPRESSIONCHANGED - ChildComponent: setting config');
console.log('EXPRESSIONCHANGED - ChildComponent: setting config ok');
If you run detectChanges
manually add logging for that too:
console.log('EXPRESSIONCHANGED - ChildComponent: running detectchanges');
this.cdr.detectChanges();
Then in Chrome debugger just filter by 'EXPRESSIONCHANGES'. This will show you exactly the flow and order of everything that gets set, and also exactly at what point Angular throws the error.
You can also click on the gray links to put breakpoints in.
Another thing to watch out if you have similarly named properties throughout your application (such as style.background
) make sure you're debugging the one you think you - by setting it to an obscure color value.
Angular runs change detection and when it finds that some values which has been passed to the child component have been changed, Angular throws the following error:
ExpressionChangedAfterItHasBeenCheckedError
click for more
In order to correct this we can use the AfterContentChecked
life cycle hook and
import { ChangeDetectorRef, AfterContentChecked} from '@angular/core';
constructor(
private cdref: ChangeDetectorRef) { }
ngAfterContentChecked() {
this.cdref.detectChanges();
}