ExpressionChangedAfterItHasBeenCheckedError Explained

AngularAngular2 ChangedetectionAngular2 Databinding

Angular Problem Overview


Please explain to me why I keep getting this error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.

Obviously, I only get it in dev mode, it doesn't happen on my production build, but it's very annoying and I simply don't understand the benefits of having an error in my dev environment that won't show up on prod --probably because of my lack of understanding.

Usually, the fix is easy enough, I just wrap the error causing code in a setTimeout like this:

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

Or force detect changes with a constructor like this: constructor(private cd: ChangeDetectorRef) {}:

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

But why do I constantly run into this error? I want to understand it so I can avoid these hacky fixes in the future.

Angular Solutions


Solution 1 - Angular

I had a similar issue. Looking at the lifecycle hooks documentation, I changed ngAfterViewInit to ngAfterContentInit and it worked.

Solution 2 - Angular

This error indicates a real problem in your application, therefore it makes sense to throw an exception.

In devMode change detection adds an additional turn after every regular change detection run to check if the model has changed.

If the model has changed between the regular and the additional change detection turn, this indicates that either

  • change detection itself has caused a change
  • a method or getter returns a different value every time it is called

which are both bad, because it is not clear how to proceed because the model might never stabilize.

If Angular runs change detection until the model stabilizes, it might run forever. If Angular doesn't run change detection, then the view might not reflect the current state of the model.

See also https://stackoverflow.com/questions/34868810/what-is-difference-between-production-and-development-mode-in-angular2/34868896#34868896

Solution 3 - Angular

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 :)

Solution 4 - Angular

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();

  }

Solution 5 - Angular

I'm using ng2-carouselamos (Angular 8 and Bootstrap 4)

Taking these steps fixed my problem:

  1. Implement AfterViewChecked
  2. Add constructor(private changeDetector : ChangeDetectorRef ) {}
  3. Then ngAfterViewChecked(){ this.changeDetector.detectChanges(); }

Solution 6 - Angular

Update

I highly recommend starting with the OP's self response first: properly think about what can be done in the constructor vs what should be done in ngOnChanges().

Original

This is more a side note than an answer, but it might help someone. I stumbled upon this problem when trying to make the presence of a button depend on the state of the form:

<button *ngIf="form.pristine">Yo</button>

As far as I know, this syntax leads to the button being added and removed from the DOM based on the condition. Which in turn leads to the ExpressionChangedAfterItHasBeenCheckedError.

The fix in my case (although I don't claim to grasp the full implications of the difference), was to use display: none instead:

<button [style.display]="form.pristine ? 'inline' : 'none'">Yo</button>

Solution 7 - Angular

There were interesting answers but I didn't seem to find one to match my needs, the closest being from @chittrang-mishra which refers only to one specific function and not several toggles as in my app.

I did not want to use [hidden] to take advantage of *ngIf not even being a part of the DOM so I found the following solution which may not be the best for all as it suppresses the error instead of correcting it, but in my case where I know the final result is correct, it seems ok for my app.

What I did was implement AfterViewChecked, add constructor(private changeDetector : ChangeDetectorRef ) {} and then

ngAfterViewChecked(){
  this.changeDetector.detectChanges();
}

I hope this helps other as many others have helped me.

Solution 8 - Angular

Follow the below steps:

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();     
}

Solution 9 - Angular

In my case, I had this problem in my spec file, while running my tests.

I had to change ngIf to [hidden]

<app-loading *ngIf="isLoading"></app-loading>

to

<app-loading [hidden]="!isLoading"></app-loading>

Solution 10 - Angular

I was facing the same problem as value was changing in one of the array in my component. But instead of detecting the changes on value change, I changed the component change detection strategy to onPush (which will detect changes on object change and not on value change).

import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';

@Component({
    changeDetection: ChangeDetectionStrategy.OnPush
    selector: -
    ......
})

Solution 11 - Angular

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.

Solution 12 - Angular

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.

Solution 13 - Angular

@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 :

  • Your parent component is created
  • The ImageCarousel component is created, and assigned to carousel (via ViewChild)
  • We can't access carousel until ngAfterViewInit() (it will be null)
  • We assign the configuration, which sets style_groupBG = 'red'
  • This in turn sets background: red on the host ImageCarousel component
  • This component is 'owned' by your parent component, so when it checks for changes it finds a change on carousel.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.

Solution 14 - Angular

Here's my thoughts on what is happening. I have not read the documentation but am sure this is part of why the error is shown.

*ngIf="isProcessing()" 

When using *ngIf, it physically changes the DOM by adding or removing the element every time the condition changes. So if the condition changes before it is rendered to the view (which is highly possible in Angular's world), the error is thrown. See explanation here between development and production modes.

[hidden]="isProcessing()"

When using [hidden] it does not physically change the DOM but merely hiding the element from the view, most likely using CSS in the back. The element is still there in the DOM but not visible depending on the condition's value. That is why the error will not occur when using [hidden].

Solution 15 - Angular

Debugging tips

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.

enter image description here

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.

Solution 16 - Angular

If you are seeing ExpressionChangedAfterItHasBeenCheckedError when triggering an EventEmitter within ngAfterViewInit() then you can pass true when creating the EventEmitter to make it emit asynchronously after the current change detection cycle.

@Component({
  ...
})
export class MyComponent implements AfterViewInit {
  // Emit asynchronously to avoid ExpressionChangedAfterItHasBeenCheckedError
  @Output() valueChange = new EventEmitter<number>(true);

  ...

  ngAfterViewInit(): void {
    ...
    this.valueChange.emit(newValue);
    ...
  }

}

Solution 17 - Angular

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

Solution 18 - Angular

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.

Solution 19 - Angular

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();
}

Solution 20 - Angular

For my issue, I was reading github - "ExpressionChangedAfterItHasBeenCheckedError when changing a component 'non model' value in afterViewInit" and decided to add the ngModel

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

It fixed my issue, I hope it helps someone.

Solution 21 - Angular

In my case, I had an async property in LoadingService with a BehavioralSubject isLoading

Using the [hidden] model works, but *ngIf fails

    <h1 [hidden]="!(loaderService.isLoading | async)">
        THIS WORKS FINE
        (Loading Data)
    </h1>

    <h1 *ngIf="!(loaderService.isLoading | async)">
        THIS THROWS ERROR
        (Loading Data)
    </h1>

Solution 22 - Angular

To anyone struggling with this. Here's a way to debug properly this error : https://blog.angular-university.io/angular-debugging/

In my case, indeed I got rid of this error using this [hidden] hack instead of *ngIf...

But the link I provided enabled me to find THE GUILTY *ngIf :)

Enjoy.

Solution 23 - Angular

I hope this helps someone coming here: We make service calls in ngOnInit in the following manner and use a variable displayMain to control the Mounting of the elements to the DOM.

component.ts

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

and component.html

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

Solution 24 - Angular

I got this error because i was using a variable in component.html which was not declared in component.ts. Once I removed the part in HTML, this error was gone.

Solution 25 - Angular

I got this error because i was dispatching redux actions in modal and modal was not opened at that time. I was dispatching actions the moment modal component recieve input. So i put setTimeout there in order to make sure that modal is opened and then actions are dipatched.

Solution 26 - Angular

My issue was I was opening up a Ngbmodal popup on load this the object that was being changed after it was checked. I was able to resolve it by opening the modal popup inside of setTimeout.

setTimeout(() => {
  this.modalReference = this.modalService.open(this.modal, { size: "lg" });
});

Solution 27 - Angular

I had this issue with between RxJS/Observables and static mock data. At first, my application used static mock data, arrays of data in my case

The html was like this:

*ngFor="let x of myArray?.splice(0, 10)"

So the idea was only display up to 10 elements from myArray. splice() takes a copy of the original array. To my knowledge this is perfectly fine in Angular.

Then I changed the data flow to Observable pattern as my 'real' data is coming from Akita (a state management library). This means my html became:

*ngFor="let x of (myArray$ | async)?.splice(0, 10)"

where myArray$ is [was] type of Observable<MyData[]>, and this data manipulation in the template is what caused the error to happen. Don't do it like this with RxJS objects.

Solution 28 - Angular

This error occurs when a value changes more than once in the same change detection cycle. I had this problem with a TypeScript getter whose return value was changing very frequently. To fix this, you can restrict a value so that it can only change once per change detection cycle as follows:

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>

The basic approach here is that each change detection cycle has its own uuid. When the uuid changes, you know that you are on the next cycle. If the cycle has changed then update the value and return it else just return the same value as previously returned on this cycle.

This ensures that each cycle returns only one value. This works fine for frequently updating values given that change detection cycles occur so frequently.

To generate the uuid I've used the uuid npm module but you can use any method that generates a unique random uuid.

Solution 29 - Angular

How does setTimeout or delay(0) fix this problem?

Here is why the code above fixes the issue:

The initial value of the flag is false, and so the loading indicator will NOT be displayed initially

ngAfterViewInit() gets called, but the data source is not immediately called, so no modifications of the loading indicator will be made synchronously via ngAfterViewInit()

Angular then finishes rendering the view and reflects the latest data changes on the screen, and the Javascript VM turn completes

One moment later, the setTimeout() call (also used inside delay(0)) is triggered, and only then the data source loads its data

the loading flag is set to true, and the loading indicator will now be displayed

Angular finishes rendering the view, and reflects the latest changes on the screen, which causes the loading indicator to get displayed

No error occurs this time around, and so this fixes the error message.

source: https://blog.angular-university.io/angular-debugging/

Solution 30 - Angular

The solution...services and rxjs...event emitters and property binding both use rxjs..you are better of implementing it your self, more control, easier to debug. Remember that event emitters are using rxjs. Simply, create a service and within an observable, have each component subscribe to tha observer and either pass new value or cosume value as needed

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKevin LeStargeView Question on Stackoverflow
Solution 1 - AngularonlymeView Answer on Stackoverflow
Solution 2 - AngularGünter ZöchbauerView Answer on Stackoverflow
Solution 3 - AngularKevin LeStargeView Answer on Stackoverflow
Solution 4 - AngularLijoView Answer on Stackoverflow
Solution 5 - AngularShahid Hussain AbbasiView Answer on Stackoverflow
Solution 6 - AngularArnaud PView Answer on Stackoverflow
Solution 7 - AngulareperView Answer on Stackoverflow
Solution 8 - AngularChittrang MishraView Answer on Stackoverflow
Solution 9 - AngularAndre EvangelistaView Answer on Stackoverflow
Solution 10 - AngularDheeraj View Answer on Stackoverflow
Solution 11 - AngularATHERView Answer on Stackoverflow
Solution 12 - AngularSafal PillaiView Answer on Stackoverflow
Solution 13 - AngularSimon_WeaverView Answer on Stackoverflow
Solution 14 - AngularKobusView Answer on Stackoverflow
Solution 15 - AngularSimon_WeaverView Answer on Stackoverflow
Solution 16 - AngulartedwView Answer on Stackoverflow
Solution 17 - Angulargoku_da_masterView Answer on Stackoverflow
Solution 18 - AngularJGFMKView Answer on Stackoverflow
Solution 19 - AngularSandeep K NairView Answer on Stackoverflow
Solution 20 - AngularDemodaveView Answer on Stackoverflow
Solution 21 - AngularMaheshView Answer on Stackoverflow
Solution 22 - AngularDeunzView Answer on Stackoverflow
Solution 23 - Angularretr0View Answer on Stackoverflow
Solution 24 - Angularshreekar hegdeView Answer on Stackoverflow
Solution 25 - AngularMuneem HabibView Answer on Stackoverflow
Solution 26 - AngularEricView Answer on Stackoverflow
Solution 27 - AngularO-9View Answer on Stackoverflow
Solution 28 - Angulardanday74View Answer on Stackoverflow
Solution 29 - AngularSaeed SharmanView Answer on Stackoverflow
Solution 30 - Angularuser998548View Answer on Stackoverflow