421
votes

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.

28
I also face the same issue, it only happens on DEV and only affecting my console logs. Instead of introducing any new code to the project, I simply hide/block this error from the console.Vaibhav
Official guideline from Angular - angular.io/errors/NG0100Alex Klaus

28 Answers

161
votes

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

128
votes

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 What is difference between production and development mode in Angular2?

115
votes

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

75
votes

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

  }
65
votes

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

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>
38
votes

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.

33
votes

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

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>
23
votes

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: -
    ......
})
19
votes

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.

6
votes

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

5
votes

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.

3
votes

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].

2
votes

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.

2
votes

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

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>
1
votes

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.

1
votes

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

0
votes

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.

0
votes

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>
0
votes

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.

0
votes

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.

0
votes

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" });
});
0
votes

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.

-1
votes

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.

-2
votes

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

-9
votes

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!