How to handle one of Angular's most common errors

Did you know that ExpressionChangedAfterItHasBeenCheckedError is one of the most common error in Angular? If you are an Angular developer, I'm sure you came across this error at some point. Keep reading, and find out how to solve this error and stop it from reproducing.

How to handle one of Angular's most common errors

Although the name is quite long, breaking it down and reading it word by word, we get an idea of what causes the issue. A certain expression or value, somewhere, was checked after it has been changed and causes the error.

But there are still questions that need answering, such as:

  • How and why is the expression being checked?
  • Where and when was it changed?
  • Why is it showing this error and how did it happen?
  • How to solve it and stop it from reproducing?

To answer these questions we have to take a brief look into Angular change detection and a couple of lifecycle hooks and see what is causing the issue.

How and why is the expression being checked?

To answer this, we first need to understand Angular change detection, as it is one of the basic features of Angular.

Whenever we are changing some code in the project, Angular can automatically detect and re-render the whole application so that the changes are instantly visible. Often the data that needs to be shown is not available when rendering the page for the first time, or as we use the application, the data changes.

As HTML is a static markup language, we need Angular to know when it needs to change the displayed data so everything is up to date. That is why every component has a set of listeners attached to it, and the listeners are looking for possible changes in the files. Angular change detection is listening to every expression in the code and as soon as one of those listeners evaluates that the value it has read is different from the previous value of that property, re-rendering will happen, so that the application has everything up to date.

Where and when was the expression changed?

This type of error can happen anywhere in the application, but it usually shows in a couple of places. The first and the most common one is when using the ngAfterViewInit lifecycle hook to change data. Angular has several hooks that are called at specific points during the lifecycle of a component and can be used for data change, but ngAfterViewInit is not one of them. It is primarily used for accessing some parts of the template using decorators, such as ViewChild or ViewChildren, but not to alter the existing data.

...
loading = true

ngOnInit(): void {
	this.exampleAPICall().pipe(
		map(response => {
		// block of code that changes the template
	})
	).toPromise()
}

ngAfterViewInit(): void {
  this.loading = false
}

Another possibility is with using a Javascript library in a way that directly changes DOM content. Those are some changes that Angular can not always detect, so sometimes while using JQuery or UmbrellaJS, it doesn’t act properly.

Why is it showing this error and how did it happen?

There is a thing that needs to be known about this error, it will only occur in the development, never in production. This is because in development mode when a change has happened, Angular change detection will run one additional time, which does not happen in the production environment.

This is to check that the data has not changed in the meantime. If it has, Angular will throw the error as the data has not stabilized, it keeps changing. This scenario can result in the change detection finding changed expressions forever, and cause the application to reload indefinitely.

How to solve it and stop it from reproducing?

This error can be solved in multiple ways, some simple, and some more difficult.

The simple ways come from adding a new piece of code to the component, in order to “trick” the change detection process. For example, one way of dealing with this error is wrapping the piece of code that is placed in ngAfterViewInit in a setTimeout() method. This allows Angular to finish rendering the view with no errors, as the setTimeout() method will not execute in the current event loop, but in the next one. Only when the rendering has finished and the change detection cycle has ended, another one will start and the setTimeout() method will be called.

ngAfterViewInit(): void {
	setTimeout(()=> {
		this.loading = false
	}, 0);
}

Another example is by running the change detection ourselves. This can be done by calling the detectChanges() method of the ChangeDetectorRef object. It will cause the change detection cycle to run from the beginning, and the changes that happened in the meantime will not cause the error.

constructor (private cd: ChangeDetectorRef) {
}

ngAfterViewInit(): void {
	this.loading = false
	this.cd.detectChanges()
}

But those kinds of “hotfixes” should always be the last resort if other methods fail. It is always better to try to change the current implementation of the code and find a solution that better goes with Angular. One simple example would be moving the code that changes the data from ngAfterViewInit to ngOnInit, and the error will disappear. This is because the change detection process is started after the ngOnInit hook, and so the data that the change detection is checking will be stabilized with no further changing.

...
loading = true

ngOnInit(): void {
	this.exampleAPICall().pipe(
		map(response => {
			// block of code that changes the template
		}),
		tap(() => this.loading = false)
	}).toPromise()
}

To sum it up...

Although ExpressionChangedAfterItHasBeenCheckedError is an error that at first glance looks like a really difficult one to understand and solve, it often is not the case. It shows that we misunderstood some of the most important aspects of Angular, such as change detection and the lifecycle hooks, and it would be good to solve those issues in development, so they don’t arise in production environments. By understanding where it is coming from and why it occurs, we can manage it and at the same time improve our code.