Please explain to me why I keep getting this error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.
Obviously,
I'm using ng2-carouselamos (Angular 8 and Bootstrap 4)
Taking these steps fixed my problem:
AfterViewChecked
constructor(private changeDetector : ChangeDetectorRef ) {}
ngAfterViewChecked(){ this.changeDetector.detectChanges(); }
@HostBinding
can be a confusing source of this error.For example, lets say you have the following host binding in a component
// image-carousel.component.ts
@HostBinding('style.background')
style_groupBG: string;
For simplicity, lets say this property is updated via the following input property:
@Input('carouselConfig')
public set carouselConfig(carouselConfig: string)
{
this.style_groupBG = carouselConfig.bgColor;
}
In the parent component you are programatically setting it in ngAfterViewInit
@ViewChild(ImageCarousel) carousel: ImageCarousel;
ngAfterViewInit()
{
this.carousel.carouselConfig = { bgColor: 'red' };
}
Here's what happens :
carousel
(via ViewChild)carousel
until ngAfterViewInit()
(it will be null)style_groupBG = 'red'
background: red
on the host ImageCarousel componentcarousel.style.background
and isn't clever enough to know that this isn't a problem so it throws the exception.One solution is to introduce another wrapper div insider ImageCarousel and set the background color on that, but then you don't get some of the benefits of using HostBinding
(such as allowing the parent to control the full bounds of the object).
The better solution, in the parent component is to add detectChanges() after setting the config.
ngAfterViewInit()
{
this.carousel.carouselConfig = { ... };
this.cdr.detectChanges();
}
This may look quite obvious set out like this, and very similar to other answers but there's a subtle difference.
Consider the case where you don't add @HostBinding
until later during development. Suddenly you get this error and it doesn't seem to make any sense.
A lot of understanding came once I understood the Angular Lifecycle Hooks and their relationship with change detection.
I was trying to get Angular to update a global flag bound to the *ngIf
of an element, and I was trying to change that flag inside of the ngOnInit()
life cycle hook of another component.
According to the documentation, this method is called after Angular has already detected changes:
Called once, after the first ngOnChanges().
So updating the flag inside of ngOnChanges()
won't initiate change detection. Then, once change detection has naturally triggered again, the flag's value has changed and the error is thrown.
In my case, I changed this:
constructor(private globalEventsService: GlobalEventsService) {
}
ngOnInit() {
this.globalEventsService.showCheckoutHeader = true;
}
To this:
constructor(private globalEventsService: GlobalEventsService) {
this.globalEventsService.showCheckoutHeader = true;
}
ngOnInit() {
}
and it fixed the problem :)
Follow the below steps:
1. Use 'ChangeDetectorRef' by importing it from @angular/core as follows:
import{ ChangeDetectorRef } from '@angular/core';
2. Implement it in constructor() as follows:
constructor( private cdRef : ChangeDetectorRef ) {}
3. Add the following method to your function which you are calling on an event like click of button. So it look like this:
functionName() {
yourCode;
//add this line to get rid of the error
this.cdRef.detectChanges();
}
I had this sort of error in Ionic3 (which uses Angular 4 as part of it's technology stack).
For me it was doing this:
<ion-icon [name]="getFavIconName()"></ion-icon>
So I was trying to conditionally change the type of an ion-icon from a pin
to a remove-circle
, per a mode a screen was operating on.
I'm guessing I'll have to add an *ngIf
instead.
Referring to the article https://blog.angularindepth.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4
So the mechanics behind change detection actually works in a way that both change detection and verification digests are performed synchronously. That means, if we update properties asynchronously the values will not be updated when the verification loop is running and we will not get ExpressionChanged...
error. The reason we get this error is, during the verification process, Angular sees different values then what it recorded during change detection phase. So to avoid that....
1) Use changeDetectorRef
2) use setTimeOut. This will execute your code in another VM as a macro-task. Angular will not see these changes during verification process and you will not get that error.
setTimeout(() => {
this.isLoading = true;
});
3) If you really want to execute your code on same VM use like
Promise.resolve(null).then(() => this.isLoading = true);
This will create a micro-task. The micro-task queue is processed after the current synchronous code has finished executing hence the update to the property will happen after the verification step.