ExpressionChangedAfterItHasBeenCheckedError Explained
Asked Answered
E

31

571

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.

Evaleen answered 12/4, 2017 at 16:59 Comment(7)
Everything you need to know about the ExpressionChangedAfterItHasBeenCheckedError error explains this behavior in great detailsMulholland
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.Coinage
Official guideline from Angular - angular.io/errors/NG0100Steele
The solution you mentioned is not a hacky but a proper solution. It is described here: blog.angular-university.io/angular-debuggingDrome
youtube.com/watch?v=O47uUnJjbJc. Please have a look on the YouTube video to get a quick overview and possible solutionsRaptorial
I've got a really nasty case where nothing helps me to avoid this error: [class.highlight-row]="isContainerRowHighlighted(row.id)" for CdkRow row in the Material grid...Rilke
I wonder why it doesn't tell you what expression changed. It just shows you the values which may or may not be helpful.Cinerarium
E
153

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

Evaleen answered 11/1, 2018 at 21:52 Comment(6)
My problem was similar. That I did a mistake after long hours, and defined a variable outside the ngOnInit function and constructor. This receives data changes from an observable, which is placed in the initalization function. Did the same thing as you to fix the error.Secern
Very similar all around, but I was trying to scroll (router.navigate) upon load to a fragment if present in the URL. This code was initially placed in AfterViewInit where I was receving the error, then I moved as you say to the constructor but it wasn't respecting the fragment. Moving to ngOnInit resolved :) thanks!Hultin
What if my html is bound to a getter returning time as "HH:MM" via get ClockValue() { return DateTime.TimeAMPM(new Date()) } it will eventually trip when the minutes change while detection is running, how can I fix this?Tletski
Same here. Also found that wrapping into setInterval() works also if it needs to fire after other lifetime event code.Calise
Got a very similar scenario, although my changed state was not a boolean flag but some enum (status) field. The fix was to wrap html referencing this problematic status field with "ngIf" which will check some other field already initialized.Altar
comparing 2way binding in angular vs wpf is very interest for me! a lot of painful complexity in angular only for 2way binding at a browser app!! shouldn't web client be simpler that native app client?!!Intimidate
L
230

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

Lindholm answered 27/7, 2017 at 3:57 Comment(6)
@PhilipEnc my issue was related to a change triggered by the DOM changing. When the DOM would change, the QueryList object (that came from a @ContentChildren property) would update, and inside the method that the update called it changed the two-way bound property. This created the problem I was having. Wrapping that change to the two-property with setTimeout like you show above did the trick. Thanks!Gilmore
In my case I had placed some code that changed the value of primeng grid array in ngAfterContentInit , I placed the code in ngOnInit and it worked.Tollefson
ngAfterContentChecked works here while ngAfterContentInit still throws error.Shingly
ngAfterContentChecked use but project loaded very slowBackbone
If we try to change the binded prop of ngIf before ngAfterViewInit then it wont throw error. i.e. we can set the value inside constructor, ngOnChanges, ngOninit without any error.Timaru
Caution (from comments) ngAfterContentChecked (and ngAfterViewChecked) is triggered on every Angular check. It can be fired multiple times every second. So be careful. By docs ngAfterViewInit is called once after first ngAfterContentChecked. I wouldn't use the Checked methods for init. It's a bad workaround. The perfect solution depends on the individual case. You maybe using Angular incorrectly and need to rethink the implementation. Angular can be very challenging and exhausting. But don't copy (or upvote) code you don't understand. :)Heterocyclic
R
162

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?

Rent answered 12/4, 2017 at 17:3 Comment(11)
How can I avoid seeing this error in the future? Is there a different way I need to be thinking about my code to ensure I do not make the same mistakes?Evaleen
Usually this is caused by some lifecycle callbacks like ngOnInit or ngOnChanges to modify the model (some lifecycle callbacks allow modify the model others don't, I don't remember myself exactly which one do or do not). Don't bind to methods or functions in the view, instead bind to fields and update the fields in event handlers. If you must bind to methods, ensure that they always return the same value instance as long as there wasn't actually a change. Change detection will call these methods a lot.Bowie
For anyone arriving here who gets this error using ngx-toaster library, here is the bug report: github.com/scttcper/ngx-toastr/issues/160Vistula
In my case I modified the model inside a Typescript 'getter'; notice how I enable() inside the getter:, get isUseMode() { if (this._isUseMode) { this.formGroup.enable() } } I moved enable() to the setter, and it works!Lueck
It's not necessarily a problem with the app. The fact that calling changeRef.detectChanges() is a solution / suppresses the error, is proof of this. It's like modifying state inside $scope.$watch() in Angular 1.Nayarit
I don't know Angular 1 too well but change detection in Angular 2 works quite differently. Your are right that it is not necessarily a problem, but usually cdRef.detectChanges() is only necessary in some weird edge-cases and you should looks carefully when you need it that you understand properly why.Bowie
What if my html is bound to a getter returning time as "HH:MM" via get ClockValue() { return DateTime.TimeAMPM(new Date()) } it will eventually trip when the minutes change while detection is running, how can I fix this?Tletski
That's a bad idea. Better to use interval or timer rxjs-dev.firebaseapp.com/api/index/function/interval and bind to it using asyncBowie
That's a bad idea. Better to use interval or timer rxjs-dev.firebaseapp.com/api/index/function/interval and bind to it using asyncBowie
@GünterZöchbauer I disagree with your statement "a real problem in your application". More often than not, it's just a poor design of Angular. When Angular developers realized that the mechanism they developed has holes - rather than redesigning it, they patched with this double check and exception. It's a fact that in many other frameworks developers can naturally write and put together components without coming across these counterintuitive and counterproductive issues, over-complication and exceptions. I believe Angular's parent-child model is fundamentally flawed.Germanism
@Germanism just don't use the default change detection strategy. The first mistake was that they added it at all, the 2nd that they made it the default. The 3rd, that all examples assume it is used. This is all just for easy onboarding of beginners. If you configure a linter rule that enforces OnPush everywhere and you use for example NgRx with | async pipe everywhere, you will hardly find a situation where you stumble upon this error and everything will be super fast.Bowie
E
153

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

Evaleen answered 11/1, 2018 at 21:52 Comment(6)
My problem was similar. That I did a mistake after long hours, and defined a variable outside the ngOnInit function and constructor. This receives data changes from an observable, which is placed in the initalization function. Did the same thing as you to fix the error.Secern
Very similar all around, but I was trying to scroll (router.navigate) upon load to a fragment if present in the URL. This code was initially placed in AfterViewInit where I was receving the error, then I moved as you say to the constructor but it wasn't respecting the fragment. Moving to ngOnInit resolved :) thanks!Hultin
What if my html is bound to a getter returning time as "HH:MM" via get ClockValue() { return DateTime.TimeAMPM(new Date()) } it will eventually trip when the minutes change while detection is running, how can I fix this?Tletski
Same here. Also found that wrapping into setInterval() works also if it needs to fire after other lifetime event code.Calise
Got a very similar scenario, although my changed state was not a boolean flag but some enum (status) field. The fix was to wrap html referencing this problematic status field with "ngIf" which will check some other field already initialized.Altar
comparing 2way binding in angular vs wpf is very interest for me! a lot of painful complexity in angular only for 2way binding at a browser app!! shouldn't web client be simpler that native app client?!!Intimidate
K
132

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

  }
Kratzer answered 8/11, 2018 at 14:43 Comment(7)
Although this might fix the issue, isn't this very expansive and overkilling the CD?Pad
I think this is the only answer that addresses this error being caused by passing value(s) to a child. Thanks!Bollard
@Pad Yes. Every time you touch on screen, anywhere, ngAfterContentChecked() is calledContagion
This solution helped me to solve my problem of passing back the change to app.components.ts from a child component. Thank youDejecta
do we do this in the child or parent component? just hit this same issue and trying better to understand the root problem.Amah
This solved the issue for me. I was using valueChanges on reactive forns in my ngOnit and applying your solution worked for me like a charm.Harrumph
As @Pad said, this should not be the answer, you can simply add console.log inside of ngAfterContentCheck() and see how expansive it is. This is simply working solution because detectChanges() has been called a lot.Seamark
C
80

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(); }
Caresse answered 11/12, 2019 at 17:56 Comment(3)
that's gotta be demanding in terms of processing effort - sounds like this could be a real potential for a serious slow down in an app that needs speedWeave
issue appear with me in angular 16 after i convert the project to single component mode but this solution fix my issue , Thank youHallucinate
Sick and tired of seeing these low-effort answers that just nuke your application's performance to solve a tiny problem.Betoken
A
47

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>
Atrocity answered 1/6, 2017 at 7:6 Comment(5)
My understanding of the difference between the ngIf vs. the style is that the ngIf does not include the HTML in the page until the condition is true, thus reducing the "page weight" a tiny bit while the style technique causes the HTML to always be in the page and is simply hidden or shown based on the value of form.pristine.Diphase
You might as well use [hidden] instead of the very verbose [style.display] part. :)Tsang
Why not. Although, as mentioned by @Simon_Weaver in another comment on this page, [hidden] will not always have the same behavior as display: noneAtrocity
I was showing two different buttons (logout/ login) with *ngIf in each button and this was causing the problem.Knavish
constructor was the right place for me, launching a material snack-barPropose
Z
46

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();     
}
Zach answered 1/5, 2018 at 10:27 Comment(0)
N
46

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.

Nutty answered 18/10, 2018 at 1:17 Comment(4)
won't this trigger an infinite change detection loop? i mean, you are detecting for changes after you checked.Carleencarlen
@ManuelAzar Apparently it does not. This is the ONLY solution that worked for me. AT LAST a bit of silence in my console. I was so tired of all these irrelevant change detection "errors".Tonsorial
What do you mean by suppressing the error? Isn't the change detected by the changeDetector?Amimia
@ManuelAzar There is no infinite loop because ngAfterViewChecked is only called once during the component lifecycle. Triggering a change detection cycle does NOT start the component lifecycle over again, so ngAfterViewChecked is not called a second time unless the component is re-rendered from scratch. (Note that some of the other hooks, such as ngDoCheck, WILL be triggered by extra change detection cycles because they misleadingly hook into the change detection cycle and NOT the component lifecycle directly. Using detectChanges() in a ngDoCheck will cause an infinite loop.)Harbour
S
32

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: -
    ......
})
Seethe answered 9/1, 2018 at 7:39 Comment(6)
This seemed to work for adding/removing from controls dynamically.. are there any drawbacks?Sudarium
Works like a charm in the situation I have at hand, thanks! A component was bound to a "global" object which was changed elsewhere and caused the Error to happen. This component already had an update handler for when the bound object was updated, this event handler now calls changeDetectorRef.detectChanges() in combination with the ChangeDetectionStrategy.OnPush this works as desired without the error.Waw
@RicardoSaracino did you find any drawbacks? I was wondering the same thing. I know how change detection OnPush works, but wondering if there is a gotcha that maybe I'm missing. I don't want to have to circle back.Episternum
@RicardoSaracino , Yes it has some drawbacks , you can refer this detailed link blog.angular-university.io/onpush-change-detection-how-it-worksSeethe
@BernoulliIT Thanks , I am glad that it worked for you.Seethe
Worked for me where loading ViewChild objects the first time was triggering this exception. It seems it checked the first time BEFORE assigning the object from a child, then detecting the first load as a change. Now works like a charm with Angular 14, using Angular Material elements.Kristofer
S
29

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>
Spindry answered 22/2, 2018 at 19:52 Comment(4)
About [hidden] : talkingdotnet.com/dont-use-hidden-attribute-angularjs-2Jinks
The difference here is that *ngIf changes the DOM, adding and removing the element from the page, while [hidden] changes the visibility of the item while not removing it from the DOM.Inosculate
But, this didn't really fix the real problem...?Secern
This triggered all of my validators the moment one of these hidden fields changes, not a pretty site to see everything in red!Hifi
V
24

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.

Voluntary answered 12/6, 2018 at 21:55 Comment(2)
Can you use option #3 with a style expression? I have a style expression for height that should be evaluated last, because it is based on injected content.Spirometer
Sorry just saw your comment, yea i dont see any reason why not. So that should work with style changes as well.Voluntary
N
13

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.

Negress answered 24/6, 2020 at 21:43 Comment(1)
Stumbled upon the issue dealing with the same use case. Thanks a ton.Lucero
J
12

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

Jinks answered 2/8, 2018 at 21:1 Comment(0)
D
10

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

}
Docker answered 27/1, 2022 at 15:57 Comment(0)
K
7

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

Kylynn answered 7/5, 2018 at 3:55 Comment(2)
If isProcessing() is doing the ame thing, you must use !isProcessing() for the [hidden]Woe
hidden doesn't "use CSS in the back", it's a regular HTML property. developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/…Jedidiah
J
6

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.

Jinks answered 2/8, 2018 at 21:19 Comment(0)
C
5

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

Calumny answered 11/9, 2018 at 18:50 Comment(0)
I
5

Add static: true to the @ViewChild decorator

@ViewChild(UploadComponent, { static: true }) uploadViewChild: UploadComponent;
Indivertible answered 21/6, 2022 at 13:26 Comment(0)
I
4

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();
}
Inviolate answered 21/8, 2019 at 10:6 Comment(4)
what was the problematic code? what is the solution here?Dar
Hi @Dar the issue was ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Happens when value changes are triggered when the DOM changesInviolate
Hi, I mean what did you do here to overcome the problem. You didn't use rxjs at all before or added delay(), or added startWith()? I already use rxjs with various rxjs methods but still get the error, I hope to solve the mistery :(Dar
The added delay make the error go away. It's works similarly to setTimeout.Jedidiah
T
3

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.

Tamboura answered 18/10, 2017 at 17:58 Comment(0)
H
2

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.

Hypoblast answered 13/4, 2018 at 14:57 Comment(2)
Where on that site does it say to add ngModel. And could you please elaborate why this should be helpful?Magda
When I was tracking down this issue, it led me to investigate the link. After reading the article I added the attribute and it fixed my issue. It is helpful if someone runs into the same issue.Hypoblast
T
2

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>
Thermotensile answered 25/4, 2019 at 0:47 Comment(0)
O
2

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.

Orville answered 20/4, 2021 at 8:59 Comment(0)
D
2

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/

Drome answered 23/11, 2021 at 11:57 Comment(0)
K
1

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>
Karilynn answered 18/6, 2019 at 13:24 Comment(0)
T
1

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.

Toponym answered 24/6, 2019 at 7:58 Comment(0)
D
1

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.

Duckweed answered 18/7, 2019 at 7:18 Comment(0)
M
1

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" });
});
Maculation answered 4/3, 2021 at 15:49 Comment(0)
W
1

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.

Weave answered 12/10, 2021 at 14:10 Comment(0)
C
0

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

Cardinal answered 5/6, 2019 at 14:47 Comment(1)
Not only this doesn't answer the question, it's also terrible advice. Guys, please don't reimplement rxjs yourself just because you're getting Angular's CD errors. :)Jedidiah
U
0

The ExpressionChangedAfterItHasBeenCheckedError error is a runtime error that can occur when doing angular development. It indicates that a change to the component's template has been detected after the component's view has been checked, which can lead to unexpected behavior.

This error typically occurs when the component's view is being checked, and a change is made to the component's template or data bindings that causes the view to be rechecked. If you want to know what happens behind the scenes... It's not a bug, it's a feature 😁 The error is completely reverse-engineered in this article

https://blog.simplified.courses/angular-expression-changed-after-it-has-been-checked-error/

Undersurface answered 21/12, 2022 at 10:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.