How can Angular’s “Expression changed after it has been checked” error be fixed?

Our application has a custom scroll behavior, which means we have to perform some computations on the scroll. However, the problem with the scroll event is that it emits too many events at once. Manipulating data this frequently in Angular often results in an “Expression changed after it has been checked” error.

You might have encountered this error in other situations, especially when you have some code written inside `ngAfterViewInit.’ This may lead to inconsistent UI rendering and performance issues on scale.

value=10;
ngAfterViewInit(){
this.value = 100;
}

Now the question is:

  • What is the root cause of this issue?
  • How to address this issue?
  • Finally, some tips to avoid this error in the first place while developing.

Introduction

When something changes, Angular efficiently updates the DoM. This mechanism traverses the component tree and compares expressions’ current and previous values. If a change is detected, Angular updates the DOM accordingly.

What is “Expression Changed After It Has Been Checked”?

This error occurs when an expression within a template changes after Angular has completed its initial change detection cycle. When Angular detects this change, it throws the error to prevent inconsistencies.

This can happen due to asynchronous operations like HTTP requests, timers, promise resolutions, or frequent changes like event listeners.

Know More About Angular Change Detection: https://medium.com/angular-simplified/how-angular-detects-dom-changes-exploring-change-detection-strategies-c2f50f4f14ec

How to fix it?

One commonly known solution, as per Stackoverflow, to solve this issue is wrapping your code inside setTimeout.

value =10;
ngAfterViewInit(){
setTimeout(()=>{
this.value = 100;
},0);
}

This works, and most of us are probably already using this solution. Honestly, this is just a workaround, not an actual solution. In fact, Angular has not yet offered a proper solution.

All this changed with Angular’s introduction of Signals. By refactoring this code with signals, you can avoid this issue. Here is a sample snippet:

value = signal(10)
ngAfterViewInit(){
this.value.set(100)
}

How can you avoid it in the first place?

Prevention is always better than cure. Always try to code so that your application will not have this issue. These tips might help you:

Use Smart & Dumb Components pattern

  • Separate your components into smart and dumb components.
  • Dumb components will act as presentational elements without any operational logic. All mutations and business operations have to be handled by Smart components.

Use Manual Change Detection Strategy

  • If you have complex components with many asynchronous operations, consider using the OnPush change detection strategy. This strategy only triggers change detection when input properties or observables change.
@Component({
selector: 'app-my-component',
template: `
<p>Data: {{ data }}</p>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MyComponent {
constructor() {}
ngOnInit() {
// Update data asynchronously
}
}

Use async pipes

  • Use async pipes to handle asynchronous data within templates. This ensures that Angular updates the view only when the data becomes available.

Avoid Mutating Data Structures

  • Instead of mutating the actual variable, create and update a new copy. All the new instances will trigger the change detection cycle again. In practice, this is quite difficult to implement.

Conclusion

Expression changed after it has been checked” is more of an issue with our code base. Maybe we need to rethink the architecture and code quality. Of course, we’ll have solutions to resolve it, but everything comes with a cost.

Signals are a game changer for Angular and a clear indicator of its evolution towards becoming more stable and developer-friendly.

Share this article
Shareable URL
Leave a Reply

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

Read next