[typescript] Expression ___ has changed after it was checked

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?

This question is related to typescript angular

The answer is


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.


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
     }
    

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!

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

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.


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.


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.


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.


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

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?).


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.


I fixed this by adding ChangeDetectionStrategy from angular core.

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

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

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.


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

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


Examples related to typescript

TS1086: An accessor cannot be declared in ambient context Element implicitly has an 'any' type because expression of type 'string' can't be used to index Angular @ViewChild() error: Expected 2 arguments, but got 1 Typescript: No index signature with a parameter of type 'string' was found on type '{ "A": string; } Understanding esModuleInterop in tsconfig file How can I solve the error 'TS2532: Object is possibly 'undefined'? Typescript: Type 'string | undefined' is not assignable to type 'string' Typescript: Type X is missing the following properties from type Y length, pop, push, concat, and 26 more. [2740] Can't perform a React state update on an unmounted component TypeScript and React - children type?

Examples related to angular

error NG6002: Appears in the NgModule.imports of AppModule, but could not be resolved to an NgModule class error TS1086: An accessor cannot be declared in an ambient context in Angular 9 TS1086: An accessor cannot be declared in ambient context @angular/material/index.d.ts' is not a module Why powershell does not run Angular commands? error: This is probably not a problem with npm. There is likely additional logging output above Angular @ViewChild() error: Expected 2 arguments, but got 1 Schema validation failed with the following errors: Data path ".builders['app-shell']" should have required property 'class' Access blocked by CORS policy: Response to preflight request doesn't pass access control check origin 'http://localhost:4200' has been blocked by CORS policy in Angular7