In this post, we will cover in detail an error message that you will occasionally come across while building Angular applications: "Expression has changed after it was checked" -
We are going to give a complete explanation about this error. We will learn why it occurs, how to debug it consistently and how to fix it.
Most of all we are going to explain why this error is useful, and how it ties back to the Angular Development Mode.
Table Of Contents
In this post, we will cover the following topics:
- Understanding the "Expression has changed" error, why does it occur?
- The Angular Development mode
- Debugging Techniques for finding the template expression that is causing the error
- How to fix the "Expression has changed" error
We will first start by quickly debugging the error (a video is available for this), and then we will explain the cause and apply the fix. So without further ado, let's get started!
A common scenario where the error occurs
This type of error usually shows up beyond the initial development stages, when we start to have some more expressions in our templates, and we have typically started to use some of the lifecycle hooks like
Here is a simple example of a component that is already throwing this error, taken from this previous post:
This is a simple component that is displaying an Angular Material Data Table with a paginator, plus a loading indicator that get's displayed while we wait for the data to load.
Here is what this component looks like when the data is loaded:
And here is what the component looks like when the data is loading:
To find out why the "Expression has changed" error is being thrown in this situation, let's have a look at the simplified version of this component:
As we can see, we are using
ngAfterViewInit(), because we want to get a reference to the Paginator
page Observable, and the paginator is obtained using the
@ViewChild() view query.
Whenever the user hits the paginator navigation buttons, an event is going to be emitted that triggers the loading of a new data page, by calling
Note that the
tapoperator is the new pipeable version of the RxJs
page Observable does not initially emit a value, we are emitting an initial value using
startWith(). This causes the first page of data to be loaded, otherwise, data would only be loaded if the user clicks the paginator.
And here is the Data Source (simplified version):
As we can see,
loadLessons() is emitting a new value for the
loading$ Observable (it's setting the loading flag to true), and is doing so synchronously, before the asynchronous call to the backend.
Notice that this
loading$ Observable is the one that is getting used in the
ngIf expression that shows or hides the loading indicator.
Error Message Example
The code above will throw the following change detection error:
CourseComponent.html:13 ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: '[object Object]'. Current value: 'true'. at viewDebugError (core.js:9515) at expressionChangedAfterItHasBeenCheckedError (core.js:9493) at checkBindingNoChanges (core.js:9662)
This error tells us that there is a problem with an expression in the template, but the question is, which expression? And why does it cause an error?
Debugging "Expression has changed after it was checked"
The debugging process that we will go over below is also done here step by step in this video, where we also explain the cause of the error:
Here is how we can identify the problematic expression. In the Chrome Dev Tools console, we have a call stack that identifies where exactly the error occurred.
Let's click on the link available in the first line of the call stack:
at viewDebugError (core.js:9515)
If we now reload the component and trigger the error again, the breakpoint will hit and we will get the following:
The program is now frozen at this point, and we can hover over the variables and go up and down the call stack, to see what is going on.
Notice the line 9515: that is where the error occurs, and the line number with the blue triangle is where we clicked to create the breakpoint.
We also have a call stack. If we start clicking on the functions up the call stack, we will see the function call to
Identifying the previous value of the Expression
By highlighting the
oldValue variable, we can see that the old value of the problematic expression was false, and according to the message it's now true:
Identifying the Problematic Expression
But what template expression is causing this error? If we keep clicking up the call stack, we are going to see that a template expression will appear:
As we can see, this is the
ngIf expression that shows or hides the loading indicator: so this is the problematic template expression!
As we can see, the source maps generated by the Angular CLI are very useful to troubleshoot this kind of problem.
Understanding the "Expression has changed after it was checked" Error
ngIf expression, at first sight, does not seem problematic. So why does this throw an error? Here is what happens:
ngIfexpression above is initially false because the data source is not loading any data, so
- if the
loading$Observable last emitted value is false, then the loading indicator should be hidden as no data is being loaded
- while Angular is preparing to update the View based on the latest data changes, during that process it calls
ngAfterViewInit, which triggers the loading of the first data page from the backend
- loading the data would still take a while and it's an asynchronous operation, so the data will not arrive instantly
- Here is the problem: as a synchronous call to
dataSource. loadLessons()is made, a new
truevalue of the
loading$flag is emitted immediately
And its this new value of the loading flag that accidentally triggers the error!
Let's learn why updating this flag during the view construction process is problematic.
The "View Updates Itself" Scenario
The problem here is that we have a situation where the view generation process (which
ngAfterViewInit is a part of) is itself further modifying the data that we are trying to display in the first place:
- the loading flag starts with false
- we tried to display that to the screen, by hiding the loading indicator
- due to the way
ngAfterViewInitis written, the act of displaying the data itself further modifies the data
- after the view is built, the loading flag is now true
So which value is the loading flag then, true or false? There is no way for Angular to decide and so it preventively throws this error, which only happens in Development Mode.
To learn more about the Angular Development Mode, have a look at this post. Right now, let's then see how we can fix this issue.
Understanding the Solution
So here is the solution: we can't use the
paginator.page reference in
ngAfterViewInit() and immediately call the Data Source, because that will trigger a further modification of the data before Angular could display it on the screen, so its not clear if the value of the loading flag should be true or false.
In order to solve this issue, we need to let Angular first display the data with the loading flag set to false.
loadLessons() method, which will cause the loading flag to be set to true and the loading indicator will then get displayed.
Initial implementation of the solution
In order to defer the code inside
This already solves the problem: we don't have an error anymore!
As we can see, we are using
Let's now have a look at an alternative implementation with less code nesting, and then we will explain why this fixes the issue.
An alternative using RxJs
This is an alternative version that looks better due to less code nesting, and uses the RxJs pipeable operator
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
- 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.
Moving the initialization of the data to
But in this case, an even better solution exists! The core of the problem is that we are modifying the data being displayed (the loading flag) inside
So let's remove the call to
startWith(null) that loads the initial page, and instead, lets trigger the loading of the initial data in
This also solves the issue, with this we don't have the error anymore.
With this new version, there is no modification of the template data in the
ngAfterViewInit() lifecycle hook, and so the problem does not occur.
Let's now wrap things up by talking about what would happen if this error would NOT be thrown.
In summary, Angular protects us from building programs that are hard to maintain in the long-term and reason about, by throwing the error "Expression has changed after it was checked" (only in development mode).
Although a bit surprising at first sight, this error is very helpful!
Why "Expression has changed after it was checked" is useful
What would happen if the view generation process could itself modify the rendered data? This could be very problematic, to start we could even create an infinite loop!
More commonly, here is what would happen: imagine having a UI that behaves in an erratic way, where sometimes the user cannot see the data in our component, and randomly sees some previous version of the data.
Then the user clicks or hovers some unrelated UI elements which happen to trigger an event handler, and now another unrelated components is affected. This kind of error can be very hard to reproduce, troubleshoot and reason about.
One of the main goals of using a web framework like Angular is the guarantee that the data in our components will always get reflected correctly in the view, and that we don't have to do that synchronization ourselves.
The Angular Development Mode helps us to avoid building UIs that are hard to troubleshoot and reason about, by issuing this error message during development that helps us to fix the issue early in the development process.
I hope that this post helps with this type of error and that you enjoyed it! If you have some questions or comments please let me know in the comments below and I will get back to you.
To get notified of upcoming posts on multiple Angular topics, I invite you to subscribe to our newsletter:
Video Lessons Available on YouTube
Have a look at the Angular University Youtube channel, we publish about 25% to a third of our video tutorials there, new videos are published all the time.
Subscribe to get new video tutorials: