440

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?

SnareChops
  • 13,175
  • 9
  • 69
  • 91
drew moore
  • 31,565
  • 17
  • 75
  • 112
  • 10
    The article [Everything you need to know about the `ExpressionChangedAfterItHasBeenCheckedError` error](https://hackernoon.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4) explains the behavior in great details. – Max Koretskyi Jul 06 '17 at 11:15
  • Consider modifying your `ChangeDetectionStrategy` when using `detectChanges()` https://stackoverflow.com/questions/39787038/how-to-manage-angular2-expression-has-changed-after-it-was-checked-exception-w/54954374#54954374 – luiscla27 Oct 17 '19 at 21:46
  • Just think about there is an input control and you are populating data to it in a method and in the same method you are assigning some value to it. The compiler will get confused for sure with the new/ previous value. So binding and populating should happen in different methods. – Ram May 14 '20 at 08:26
  • Does this answer your question? [How to manage Angular2 "expression has changed after it was checked" exception when a component property depends on current datetime](https://stackoverflow.com/questions/39787038/how-to-manage-angular2-expression-has-changed-after-it-was-checked-exception-w) – luiscla27 Jan 10 '22 at 18:34
  • As also asked by @Jo-VdB, can't you use ngOnInit() to update bindings ? – oomer Aug 09 '22 at 22:08

23 Answers23

431

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.

About7Deaths
  • 681
  • 1
  • 5
  • 16
Kiara Grouwstra
  • 5,723
  • 4
  • 21
  • 36
  • 12
    In my case I was opening a modal. After open the modal it was showing the message "Expression ___ has changed after it was checked", so my solution was added this.cdr.detectChanges(); after open my modal. Thanks! – Jorge Casariego Sep 08 '16 at 15:38
  • Where is your declaration for the cdr property? I would expect to see a line like `cdr : any` or something like that under the `message` declaration. Just worried I've missed something? – CodeCabbie Mar 09 '17 at 10:54
  • 1
    @CodeCabbie it's in the constructor arguments. – slasky Mar 31 '17 at 23:08
  • 3
    This resolution fix my problem! Thanks a lot! Very clear and simple way. – wozniaklukasz Oct 31 '17 at 07:26
  • 8
    This helped me - with a few modifications. I had to style li elements that were generated via ngFor loop. I needed to change the color of spans within the list items based on innerText upon clicking 'sort' which updated a bool being used as a parameter of a sort pipe (the sorted result was a copy of the data, so styles weren't getting updated with ngStyle alone). - Instead of using 'AfterViewInit', I used **'AfterViewChecked'** - I also made sure to *import* and *implement* AfterViewChecked. *note: setting the pipe to 'pure: false' was not working, I had to add this extra step (:* – Chloe Corrigan Jul 20 '18 at 14:25
  • I had to use the AfterViewChecked instead of the AfterViewInit as well. – user906573 Nov 21 '19 at 15:42
  • I was using `AfterViewInit` but I forgot to inject `ChangeDetectorRef`. This answer saved me today. thanks :-) – Tanzeel Feb 15 '20 at 09:25
  • This resolution fixed my problem with a mat-checkbox. Thanks mate! – Francisco do Vale Aug 07 '20 at 13:19
  • I put ChangeDetectorRef() one line after the variable that changes and works, thanks! – Edward Ramos Jul 30 '22 at 15:06
  • 1
    The framework itself could have known that the change happened in a lifecycle method (ngAfterViewInit) and could have called the additional change detection by itself. Leaving it to the user of the framework to tie up the loose ends is poor design. Scratching head emoji. – rapt Dec 02 '22 at 08:47
219

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 :)')
  }
}
Community
  • 1
  • 1
drew moore
  • 31,565
  • 17
  • 75
  • 112
  • 18
    Why doesn't setMessage() trigger a new round of change detection? I thought Angular 2 automatically triggered change detection when you change the value of something in the UI. – Daynil Dec 20 '15 at 20:11
  • 5
    @drewmoore "Anything that changes a binding needs to trigger a round of change detection when it does". How? Is it a good practice? Shouldn't everything be completed in a single run? – Daniel Birowsky Popeski Dec 25 '15 at 12:04
  • @MarcJ.Schmidt, to manually run change detection, see [this answer](http://stackoverflow.com/a/33821833/215945). I had a component configured with with `onPush` change detection, so I also had to call `markForCheck()` on an injected ChangeDetectorRef before calling `tick()` on the injected ApplicationRef for the view to update. – Mark Rajcok Dec 30 '15 at 02:52
  • @MarkRajcok: the `tick` approach you linked runs change detection for the whole application. That's fine for the current question, but may yield performance problems in larger applications. In those cases you can instead use `ChangeDetectorRef`'s `detectChanges()`; see my answer for a fixed version of the question's plunker. – Kiara Grouwstra Feb 06 '16 at 16:05
  • 2
    @Tycho, indeed. Since I wrote that comment, I've answered another question where I describe the [3 ways to run change detection](http://stackoverflow.com/a/35106069/215945), which includes `detectChanges()`. – Mark Rajcok Feb 06 '16 at 16:51
  • 4
    just to note that, in current question body, the invoked method is named `updateMessage`, not `setMessage` – superjos Aug 22 '16 at 09:53
  • Hi. Stupid question: this is happening to me if I try to set the focus from within the AfterView event: window.setTimeout( () => this._servico.input.focus(), 0 );. Changing the interval to 100ms seems to make the error go away. why? thanks, – Luis Abreu Apr 09 '17 at 15:04
  • In my project i had to inject the ChangeDetectorRef in my component and call its detectChanges methods – Rebolon Apr 10 '17 at 12:58
  • your plunker shows the expression has been changed after check- error – Pascal Sep 13 '17 at 13:26
  • 3
    @Daynil, I had the same feeling, until I read the blog given in comment under the question: https://blog.angularindepth.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4 It explains why this needs to be manually done. In this case, angular's change detection has a lifecycle. In case something is changing a value in between these lifecycles, then a forceful change detection needs to be run (or a settimeout - which executes in next event loop, triggering the change detection again). – Mahesh Nov 16 '17 at 02:30
  • calling enableProdMode() fixed my issue – Robin Raju Mar 15 '18 at 17:57
  • 1
    This is the kind of team leader I want to work under. Kudos. – Timotronadon Mar 18 '21 at 13:38
  • Working link to the blog @Mahesh posted https://indepth.dev/posts/1001/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error – BrunoElo Nov 03 '21 at 11:09
  • We use behavioursubjects for our loaders everywhere and it used to work perfectly fine. Than I removed hybrid and now I have these random not always occurring errors of this message... eventhough everything is properly done in the ngOnInit #magic – Samantha Adrichem Jan 03 '23 at 08:46
93

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();
}
Amit Soni
  • 3,216
  • 6
  • 31
  • 50
Jaswanth Kumar
  • 3,531
  • 3
  • 23
  • 26
  • 1
    As drew mentioned, the angular's change detection cycle which is of two phase, has to detect changes after the child's view is modified, and I feel the best way to do that is using the lifecycle hook provided by angular itself, and asking angular to manually detect the change and bind it. My personal opinion is this seems an appropriate answer. – vijayakumarpsg587 Oct 03 '19 at 10:40
  • 1
    This work for me, for loading hierarchy dynamic component inside together. – Mehdi Daustany Mar 22 '20 at 00:01
  • 1
    The only problem is it will run every time whenever you perform any action on screen – Kanchan Tyagi Feb 11 '22 at 11:26
66

I fixed this by adding ChangeDetectionStrategy from angular core.

import {  Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  selector: 'page1',
  templateUrl: 'page1.html',
})
Biranchi
  • 16,120
  • 23
  • 124
  • 161
  • This worked for me. I wonder what's the difference between this and using ChangeDetectorRef – Ari Aug 31 '17 at 04:56
  • 1
    Hmm... Then the change detector's mode will be initially set to `CheckOnce` ([documentation](https://angular.io/api/core/ChangeDetectionStrategy)) – Alex Klaus Dec 08 '17 at 04:29
  • Yes, the error/warning is gone. But it is taking way much loading time than before like 5-7 seconds difference which is huge. – Kapil Raghuwanshi Jun 28 '19 at 11:36
  • 1
    @KapilRaghuwanshi Run `this.cdr.detectChanges();` after whatever you are trying to load. Because it might be because change detection is not triggering – Harshal Carpenter Dec 11 '19 at 23:19
  • dont use `changeDetection: ChangeDetectionStrategy.OnPush,` this will prevent lifecycle between html and ts – Mohammad Mehdi Mohammadi Mar 05 '21 at 20:21
54

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);
}
Jo VdB
  • 2,016
  • 18
  • 16
48

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.

MarmiK
  • 5,639
  • 6
  • 40
  • 49
42

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
     }
    
Prashant Pimpale
  • 10,349
  • 9
  • 44
  • 84
Shahid Hussain Abbasi
  • 2,508
  • 16
  • 10
  • 1
    Yup this worked for me too. I was using AfterViewInit. – abrsh Aug 13 '20 at 14:51
  • 1
    This works and I guess this should be the correct answer. – Silambarasan R Sep 17 '20 at 06:05
  • 2
    To me using `ngAfterContentChecked` doesn't seem to be the smart thing to do regarding performance. In my lttle sample the code will be executed multiple times, even in you scroll after angular finished the view init.Any thoughts on performance? – Smamatti Sep 21 '20 at 09:31
27

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.

Max Koretskyi
  • 101,079
  • 60
  • 333
  • 488
  • 1
    Read your articles: https://blog.angularindepth.com/a-gentle-introduction-into-change-detection-in-angular-33f9ffff6f10, Will read another one https://blog.angularindepth.com/everything-you-need-to-know-about-change-detection-in-angular-8006c51d206f soon. Still could not figure out solution for this problem. can you tell me what happens if I add ngDoCheck or ngAfterContentChecked lifecycle hook and add this this.cdr.markForCheck(); (cdr for ChangeDetectorRef) inside it. Isn't it a right way to check for changes after life cycle hook and subsequent check completes. – Always_a_learner Apr 01 '19 at 10:10
  • Read your article and Promise.then solved my issue. BTW, it was happening when I comment out `enableProdMode();` , I mean in debug time. NG6, in prod it was not happening but creating a microtask make sense.. – Davut Gürbüz Aug 12 '20 at 15:39
19

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

sharad jain
  • 1,471
  • 13
  • 9
  • Checkout this best content for JS CONCEPTS -> https://www.youtube.com/watch?v=pN6jk0uUrD8&list=PLlasXeu85E9cQ32gLCvAvr9vNaUccPVNP – Suprabhat Kumar May 11 '22 at 09:37
13

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.

RedPelle
  • 285
  • 4
  • 17
  • 1
    I was using a combination of a [`@ViewChildren()` length-based counter](http://stackoverflow.com/a/37243960/114900) which was populated by an Observable. This was the only solution that worked for me! – msanford Mar 13 '17 at 20:02
  • 2
    Combination of `ngAfterContentChecked` and `ChangeDetectorRef` from above worked for me. On `ngAfterContentChecked` called - `this.cdr.detectChanges();` – Kunal Dethe Mar 03 '18 at 12:09
5

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

Tobias Gassmann
  • 11,399
  • 15
  • 58
  • 92
5

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.
}
WasiF
  • 26,101
  • 16
  • 120
  • 128
4

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());
rabinneslo
  • 61
  • 1
3

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

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.
}
Sunny Vakil
  • 362
  • 4
  • 7
2

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

ngOnInit(): void { 
  this.updateMessage();
}
blackgreen
  • 34,072
  • 23
  • 111
  • 129
ASC User
  • 31
  • 2
1

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.

Yusuff Sodiq
  • 815
  • 2
  • 12
  • 19
1

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

PeterPazmandi
  • 533
  • 10
  • 13
0

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.

TKDev
  • 493
  • 1
  • 4
  • 19
0

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.

Paul LeBeau
  • 97,474
  • 9
  • 154
  • 181
Priyanka Arora
  • 409
  • 4
  • 10
0

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!
Imran Faruqi
  • 663
  • 9
  • 19
0

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.

Felix
  • 1,662
  • 2
  • 18
  • 37
0

just move the code to constructor of the component in which you are changing the shared service subject