Expression ___ has changed after it was checked

TypescriptAngular

Typescript Problem Overview


Why is the component in this simple plunk

@Component({
  selector: 'my-app',
  template: `<div>I'm {{message}} </div>`,
})
export class App {
  message:string = 'loading :(';

  ngAfterViewInit() {
    this.updateMessage();
  }
  
  updateMessage(){
    this.message = 'all done loading :)'
  }
}

throwing:

> EXCEPTION: Expression 'I'm {{message}} in App@0:5' has changed after it was checked. Previous value: 'I'm loading :( '. Current value: 'I'm all done loading :) ' in [I'm {{message}} in App@0:5]

when all I'm doing is updating a simple binding when my view is initiated?

Typescript Solutions


Solution 1 - Typescript

As stated by drewmoore, the proper solution in this case is to manually trigger change detection for the current component. This is done using the detectChanges() method of the ChangeDetectorRef object (imported from angular2/core), or its markForCheck() method, which also makes any parent components update. Relevant example:

import { Component, ChangeDetectorRef, AfterViewInit } from 'angular2/core'

@Component({
  selector: 'my-app',
  template: `<div>I'm {{message}} </div>`,
})
export class App implements AfterViewInit {
  message: string = 'loading :(';

  constructor(private cdr: ChangeDetectorRef) {}
  
  ngAfterViewInit() {
    this.message = 'all done loading :)'
    this.cdr.detectChanges();
  }
  
}

Here are also Plunkers demonstrating the ngOnInit, setTimeout, and enableProdMode approaches just in case.

Solution 2 - Typescript

First, note that this exception will only be thrown when you're running your app in dev mode (which is the case by default as of beta-0): If you call enableProdMode() when bootstrapping the app, it won't get thrown (see updated plunk).

Second, don't do that because this exception is being thrown for good reason: In short, when in dev mode, every round of change detection is followed immediately by a second round that verifies no bindings have changed since the end of the first, as this would indicate that changes are being caused by change detection itself.

In your plunk, the binding {{message}} is changed by your call to setMessage(), which happens in the ngAfterViewInit hook, which occurs as a part of the initial change detection turn. That in itself isn't problematic though - the problem is that setMessage() changes the binding but does not trigger a new round of change detection, meaning that this change won't be detected until some future round of change detection is triggered somewhere else.

The takeaway: Anything that changes a binding needs to trigger a round of change detection when it does.

Update in response to all the requests for an example of how to do that: @Tycho's solution works, as do the three methods in the answer @MarkRajcok pointed out. But frankly, they all feel ugly and wrong to me, like the sort of hacks we got used to leaning on in ng1.

To be sure, there are occasional circumstances where these hacks are appropriate, but if you're using them on anything more than a very occasional basis, it's a sign that you're fighting the framework rather than fully embracing its reactive nature.

IMHO, a more idiomatic, "Angular2 way" of approaching this is something along the lines of: (plunk)

@Component({
  selector: 'my-app',
  template: `<div>I'm {{message | async}} </div>`
})
export class App {
  message:Subject<string> = new BehaviorSubject('loading :(');

  ngAfterViewInit() {
    this.message.next('all done loading :)')
  }
}

Solution 3 - Typescript

ngAfterViewChecked() worked for me:

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

constructor(private cdr: ChangeDetectorRef) { }
ngAfterViewChecked(){
   //your code to update the model
   this.cdr.detectChanges();
}

Solution 4 - Typescript

I fixed this by adding ChangeDetectionStrategy from angular core.

import {  Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  selector: 'page1',
  templateUrl: 'page1.html',
})

Solution 5 - Typescript

Can't you use ngOnInit because you just changing the member variable message?

If you want to access a reference to a child component @ViewChild(ChildComponent), you indeed need to wait for it with ngAfterViewInit.

A dirty fix is to call the updateMessage() in the next event loop with e.g. setTimeout.

ngAfterViewInit() {
  setTimeout(() => {
    this.updateMessage();
  }, 1);
}

Solution 6 - Typescript

For this I have tried above answers many does not work in latest version of Angular (6 or later)

I am using Material control that required changes after first binding done.

	export class AbcClass implements OnInit, AfterContentChecked{
		constructor(private ref: ChangeDetectorRef) {}
		ngOnInit(){
			// your tasks
		}
		ngAfterContentChecked() {
			this.ref.detectChanges();
		}
	}

Adding my answer so, this helps some solve specific issue.

Solution 7 - Typescript

I switched from AfterViewInit to AfterContentChecked and It worked for me.

Here is the process

  1. Add dependency in your constructor:

    constructor (private cdr: ChangeDetectorRef) {}

  2. and call your login in implemented method code here:

     ngAfterContentChecked() {
         this.cdr.detectChanges();
      // call or add here your code
     }
    

Solution 8 - Typescript

The article Everything you need to know about the ExpressionChangedAfterItHasBeenCheckedError error explains the behavior in great details.

The problem with you setup is that ngAfterViewInit lifecycle hook is executed after change detection processed DOM updates. And you're effectively changing the property that is used in the template in this hook which means that DOM needs to be re-rendered:

  ngAfterViewInit() {
    this.message = 'all done loading :)'; // needs to be rendered the DOM
  }

and this will require another change detection cycle and Angular by design only runs one digest cycle.

You basically have two alternatives how to fix it:

  • update the property asynchronously either using setTimeout, Promise.then or asynchronous observable referenced in the template

  • perform the property update in a hook before the DOM update - ngOnInit, ngDoCheck, ngAfterContentInit, ngAfterContentChecked.

Solution 9 - Typescript

This error is coming because existing value is getting updated immediately after getting initialized. So if you will update new value after existing value is rendered in DOM, Then it will work fine.Like mentioned in this article Angular Debugging "Expression has changed after it was checked"

for example you can use

ngOnInit() {
    setTimeout(() => {
      //code for your new value.
    });

}

or

ngAfterViewInit() {
  this.paginator.page
      .pipe(
          startWith(null),
          delay(0),
          tap(() => this.dataSource.loadLessons(...))
      ).subscribe();
}

As you can see i have not mentioned time in setTimeout method. As it is browser provided API, not a JavaScript API, So this will run seperately in browser stack and will wait till call stack items are finished.

How browser API envokes concept is explained by Philip Roberts in one of Youtube video(What the hack is event loop?).

Solution 10 - Typescript

You just have to update your message in the right lifecycle hook, in this case is ngAfterContentChecked instead of ngAfterViewInit, because in ngAfterViewInit a check for the variable message has been started but is not yet ended.

see: https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html#!#afterview

so the code will be just:

import { Component } from 'angular2/core'

@Component({
  selector: 'my-app',
  template: `<div>I'm {{message}} </div>`,
})
export class App {
  message: string = 'loading :(';

  ngAfterContentChecked() {
     this.message = 'all done loading :)'
  }      
}

see the working demo on Plunker.

Solution 11 - Typescript

You can also put your call to updateMessage() in the ngOnInt()-Method, at least it works for me

ngOnInit() {
	this.updateMessage();
}

In RC1 this does not trigger the exception

Solution 12 - Typescript

You can also create a timer using the rxjs Observable.timer function, and then update the message in your subscription:                    

Observable.timer(1).subscribe(()=> this.updateMessage());

Solution 13 - Typescript

It throw an error because your code get updated when ngAfterViewInit() is called. Mean your initial value got changed when ngAfterViewInit take place, If you call that in ngAfterContentInit() then it will not throw an error.

ngAfterContentInit() {
    this.updateMessage();
}

Solution 14 - Typescript

I have almost same case, I have an array of products. I have to let users to remove product as per their choices. in the end if there is no product in array then I need to show Cancel button instead of the Back button without reloading page.

I got it done by checking for empty array in ngAfterViewChecked() life cycle hook. This is how I got done, Hope it helps :)

import { ChangeDetectorRef } from '@angular/core';

products: Product[];
someCondition: boolean;

constructor(private cdr: ChangeDetectorRef) {}

ngAfterViewChecked() {
  if(!this.someCondition) {
    this.emptyArray();
  }
}

emptyArray() {
    this.someCondition = this.products.length === 0 ? true : false;

    // run change detection explicitly
    this.cdr.detectChanges();
}

removeProduct(productId: number) {
    // your logic for removing product.
}

Solution 15 - Typescript

In my case, it happened with a p-radioButton. The problem was that I was using the name attribute (which wasn't needed) alongside the formControlName attribute like this:

<p-radioButton formControlName="isApplicant" name="isapplicant" value="T" label="Yes"></p-radioButton>
<p-radioButton formControlName="isApplicant" name="isapplicant" value="T" label="No"></p-radioButton>

I also had the initial value "T" bound to the isApplicant form control like this:

isApplicant: ["T"]

I fixed the problem by removing the name attributes in the radio buttons. Also, because the 2 radio buttons have the same value (T) which is wrong in my case, simply changing one of then to another value (say F) also fixed the issue.

Solution 16 - Typescript

I couldnt comment on @Biranchi s post since I dont have enough reputation, but it fixed the problem for me.

One thing to note! If adding changeDetection: ChangeDetectionStrategy.OnPush on the component didn't work, and its a child component (dumb component) try adding it to the parent also.

This fixed the bug, but I wonder what are the side effects of this.

Solution 17 - Typescript

I got similar error while working with datatable. What happens is when you use *ngFor inside another *ngFor datatable throw this error as it interepts angular change cycle. SO instead of using datatable inside datatable use one regular table or replace mf.data with the array name. This works fine.

Solution 18 - Typescript

I think a most simple solution would be as follows:

  1. Make one implementation of assigning a value to some variable i.e. via function or setter.
  2. Create a class variable (static working: boolean) in the class where this function exists and every time you call the function, simply make it true whichever you like. Within the function, if the value of working is true, then simply return right away without doing anything. else, perform the task you want. Make sure to change this variable to false once the task is completed i.e. at the end of the line of codes or within the subscribe method when you are done assigning values!

Solution 19 - Typescript

Good answers. However, it appears to me, that when I am using: ChangeDetectorRef and AfterViewInit, Angular gets into a few extra rendering cycles and if my HTML code is not designed with really good care or has a need for several calls to functions in the TS code that depend on the refresh, I get extra calls for the view rendering, thus extra processing.

Here is a solution I like using, because I do not have to worry about any of that, it is programmatically very simple, and requires nothing much extra from me or the system. I use it with no issues whenever Angular gives me hard time with the infamous error: "Expression has changed after it was checked".

I have this little public/exported function, which simply passes my value through a zero delay Promise. What that does, it forces JavaScript/JS into another background cycle, thus separating the value update into next processing cycle, and - preventing the error. (Note that the JS cycle is not the same as the Angular HTML view rendering cycle and is less processing intensive).

export async function delayValue(v: any, timeOutMs: number = 0): Promise<any> {
	return new Promise((resolve) => {
		setTimeout(() => {
		  resolve(v);
		}, timeOutMs);
	  });
}

Now, when I need to prevent the error, I simply do:

this.myClassValue = await delayValue(newValue);

which is just one line of code. There really is no notable delay since the value of timeOutMs = 0.

Here is a typical scenario:

myObservable$.subscribe(newValue  = {
    ...                // WHEN NEW VALUE ARRIVES FROM NOTIFIER(S)
    this.handleSubscribedValues(newValue);
    ...
});
                      // THIS MAY GIVE YOU THE ERROR !
private handleSubscribedValues(newValue) {
    this.myClassValue = newValue;
}
                      // SO, USE THIS INSTEAD TO AVOID THE ERROR
private async handleSubscribedValues(newValue) {
    this.myClassValue = await delayValue(newValue);
}

You can also use the delayValue() function with some delay/timeout value if you need to wait a bit for something to happen, e.g. give user a few seconds.

Hopefully, this will be useful to some of you.

Solution 20 - Typescript

I had the same error, and I could solve it by using AfterViewInit and ChangeDetectionStrategy.OnPush

Here is a detailed article. https://medium.com/@bencabanes/angular-change-detection-strategy-an-introduction-819aaa7204e7

Solution 21 - Typescript

Simple: first detach/remove the change detection in the construction of your component and then enable detectChanges() in ngAfterViewInit() method

constructor(private cdr: ChangeDetectorRef) {
  this.cdr.detach() // detach/remove the change detection here in constructor
}


ngAfterViewInit(): void {
  // do load objects or other logics here
  
  // at the end of this method, call detectChanges() method.
  this.cdr.detectChanges(); // enable detectChanges here and you're done.
}

Solution 22 - Typescript

You can also try to put this.updateMessage(); under ngOnInit, like this:

ngOnInit(): void { 
  this.updateMessage();
}

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
Questiondrew mooreView Question on Stackoverflow
Solution 1 - TypescriptKiara GrouwstraView Answer on Stackoverflow
Solution 2 - Typescriptdrew mooreView Answer on Stackoverflow
Solution 3 - TypescriptJaswanth KumarView Answer on Stackoverflow
Solution 4 - TypescriptBiranchiView Answer on Stackoverflow
Solution 5 - TypescriptJo VdBView Answer on Stackoverflow
Solution 6 - TypescriptMarmiKView Answer on Stackoverflow
Solution 7 - TypescriptShahid Hussain AbbasiView Answer on Stackoverflow
Solution 8 - TypescriptMax KoretskyiView Answer on Stackoverflow
Solution 9 - Typescriptsharad jainView Answer on Stackoverflow
Solution 10 - TypescriptRedPelleView Answer on Stackoverflow
Solution 11 - TypescriptTobias GassmannView Answer on Stackoverflow
Solution 12 - TypescriptrabinnesloView Answer on Stackoverflow
Solution 13 - TypescriptSandip - Frontend DeveloperView Answer on Stackoverflow
Solution 14 - TypescriptSunny VakilView Answer on Stackoverflow
Solution 15 - TypescriptYusuff SodiqView Answer on Stackoverflow
Solution 16 - TypescriptTKDevView Answer on Stackoverflow
Solution 17 - TypescriptPriyanka AroraView Answer on Stackoverflow
Solution 18 - TypescriptImran FaruqiView Answer on Stackoverflow
Solution 19 - TypescriptFelixView Answer on Stackoverflow
Solution 20 - Typescriptinspire_codingView Answer on Stackoverflow
Solution 21 - TypescriptWasiFView Answer on Stackoverflow
Solution 22 - TypescriptMarina HelmyView Answer on Stackoverflow