ExpressionChangedAfterItHasBeenCheckedError in Angular

The expression has changed after it was checked (Expressionchangedafterithasbeencheckederror) is one of the common errors, that we encounter in Angular applications. The error arises when the binding expression changes after angular checked it during the change detection cycle.

ExpressionChangedAfterItHasBeenCheckedError

To understand the error, let us create an app and try to produce the error.

Create a new angular application. Add child.component.ts with the following code. It has one form element message.

Open app.component.ts and add following code.

  1. We are using the ViewChild query to get the reference of the ChildComponent (childComp)
  2. this.message=this.childComp.message; updates the message property of this component with that of ChildComponent. We are not doing anything with this property.

Run this app. Type Hello it will work correctly and you will not see any errors.

Now, add the following in the template of AppComponent

Run the app. Now as soon as you start typing Hello, the Angular will throw the error ExpressionChangedAfterItHasBeenCheckedError, which you can see in the developer console.

The above error appears only in the development environment. If you enable the production environment, you will no longer see the error.

Reason for the error

To understand the error, we need to understand what is change detection & how it works.

What is Change Detection ?

Change detection (CD) is the mechanism by which angular keeps the template in sync with the component.

Now, look at the following template from the app.component.ts. Angular needs to evaluate the string interpolation expression {{message}} and replace it with the actual value and update the DOM. It must do it every time, the value of the message changes.

To do that, Angular runs a change detection process, which checks the value of every such binding in the component. If the current value of such binding is different from its previous value (oldValue), then it will update the DOM.

For the change detection work properly, the angular must run change detection on every event that may lead to a change. The following are some of the events that may lead to a change in the state of the application.

  1. Browser events like click, keyup, mouse movement, etc
  2. Child Component raising an event to notify the parent
  3. Sending HTTP Requests to the backend server for a data
  4. Observable / Promises emits a new result
  5. Timer events like setInterval() and setTimeout()

Hence, Angular starts a change detection, whenever the above events take place.

Now, there are two important points to remember

  1. Change Detection is unidirectional and always starts from the root component irrespective of where the change has occurred.
  2. It evaluates each binding only once in each change detection cycle and then it updates the DOM if necessary

Back to the Error

Now, let us see what is happening in our above code.

  1. Initially, the value of message is empty
  2. We enter h in the input element. This starts a change detection cycle.
  3. It checks the value of message property. Its value is empty. Hence updates the DOM with empty string.
  4. Angular fires the AfterViewChecked hook
  5. AfterViewChecked updates the message property to h.
  6. Angular runs another check to see if all the binding’s values are correct. It detects the value of message property is now h. which is different from when it checked it in step 3. It raises the ExpressionChangedAfterItHasBeenCheckedError
  7. Change detection cycle ends

Also, note that h is not displayed as soon as you type it. To display it we need to start another change detection, which we start just by clicking elsewhere in the browser window.

Error appears only in development mode

Angular runs another check only in the development mode. It helps us to identify & rectify the error.

Angular could easily update the bindings & DOM in the second check. But such a check may potentially introduce new changes in the bindings. Hence, we may actually enter into an infinite loop. In Fact, that is what happened in the AngularJS digest cycle.

Angular does not run another check in the production environment for performance reasons. Because It is just a waste of crucial CPU time. Hence you won’t see any errors thrown in the console window. That does not mean that error does not exist.

Examples & Solutions to Expression Changed After It Has Been Checked Error

There is no single right solution to the ExpressionChangedAfterItHasBeenCheckedError problem. The solution depends on each use case.

The most important is to find out where and why the error is happening. And based on that refactor the code.

In the example code above, instead of the parent reading the child component property, it would be better if the child component raises an event, whenever the model changes using the output & EventEmitter

In the parent component use the event binding to read the message whenever it changes in the child component.

Check AfterViewChecked & AfterContentChecked hook

Angular invokes AfterViewChecked after it checks all the bindings. AfterContentChecked is triggered when angular finishes checking of the projected content.

Hence if you are using these hooks, you should make sure that they do not change any binding that angular has already checked. Better to avoid using these hooks as angular invokes them during every change detection cycle.

For any initialisation you can make use of OnInit hook.

ViewChild & Expression Changed Error

Look at this question from stackoverflow.com. The code is shown below (also check the stackblitz.com.).

The culprit here is the boxElement, which is undefined when the component is loads for the first time. The following is the sequence of events that takes place.

  • Angular Instantiates the component. The boxElement is undefined.
  • The change detection cycle begins. (steps 1 to 6)
  • Angular starts to check the view and evaluates isShowExpand(). It returns undefined because boxElement is undefined. (step 4 )
  • Angular updates the ViewChild query and updates the boxElement. Raises the AfterViewInit & AfterViewChecked (step 5 & 6 )
  • Angular runs another check to see if all the bindings values are correct. If detects isShowExpand() method returns false Hence it raises the ExpressionChangedAfterItHasBeenCheckedError (step 7 & 8)
  • Angular runs second CD on app startup. (steps 9 to 13).
  • Data Arrives from the back end (step 15).
  • This triggers another change detection (step 16 to 20 )
  • isShowExpand() returns true (step 18)
  • Angular runs another check. isShowExpand() returns true, Hence no error is thrown.

The Stack Overflow solution uses the AfterViewChecked to trigger another change detection. But the AfterViewChecked runs on every CD, hence it is not an optimal solution.

First, we create a property show and use it to bind to the ngIf. This will make the CD quicker as it does not have to call the isShowExpand method on every CD.

Since we need to update the show property only when we receive the data, we use the fetchData method to update it.

After the data arrives we need to wait for angular to render the data. Otherwise scrollHeight & clientHeight will not be correctly updated. Hence, we use the setTimeout to wait for a tick before calling the isShowExpand method. The setTimeout triggers a change detection.

ngIf and ExpressionChangedAfterItHasBeenCheckedError

The expressions we use in ngIf is one of the common reason for ExpressionChangedAfterItHasBeenCheckedError. Consider the following example

stackblitz

  1. The age field accepts values Child (1) & Adult (2).
  2. If age is Adult (i.e. age.value===2) then married field is required else not.

Run this app and select age as Adult. The error is thrown immediately.

Error is thrown in the ngIf expression where we use married.invalid.

But the following line does not throw the error although we use the same expression married.invalid

  1. When the app starts the married.invalid is false
  2. We change the age to Adult. The age.value updated to 2. , but the married.invalid is still false. Angular is yet to update it
  3. Angular starts the change detection.
  4. ngIf is structural directive. It is treated as a child view, Hence Angular starts the change detection of ngIf first. It evaluates the married.invalid (which is false), which is an input to the ngIf directive.
  5. Once angular finishes all the child components, it will start checking the current component. Updates married.invalid to true
  6. Now, Angular runs another check It raises the error when it checks the ngIf expression.

Solution

The solution is to control the evaluation of ngIf expression. Change the expression to a property in the component class marriedinvalid.

Next, use the ModelChange to listen to the changes to the age & married input fields.

Finally, in the component code listen to the modelChanged. Update the marriedinvalid accordingly. We use the setTimeout to wait for a tick before updating the marriedinvalid.

Stackblitz.com

References

Everything you need to know about the ExpressionChangedAfterItHasBeenCheckedError error

Read More

  1. Angular Tutorial
  2. Typescript Tutorial
  3. Component Life Cycle
  4. ViewChild
  5. Input, Output & EventEmitter
  6. OnInit & OnDestroy
  7. OnChanges
  8. DoCheck
  9. AfterViewInit, AfterViewChecked, AfterContentInit & AfterContentChecked

Source Code

  1. Example 1
  2. Example 1A
  3. Example 2
  4. Example 2A
  5. Example 3

2 thoughts on “ExpressionChangedAfterItHasBeenCheckedError in Angular”

  1. Thank you for this insightful post! The detailed explanation of the “ExpressionChangedAfterItHasBeenCheckedError” and the practical examples really helped clarify the concept for me. I appreciate the tips on how to resolve this error. Looking forward to more posts like this!

  2. Using setTimeout to “make something work” is code smell. Also there is no guarantee you will have awaited the next “tick”.

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Scroll to Top