Fix ExpressionChangedAfterItHasBeenCheckedError in Angular?

The ExpressionChangedAfterItHasBeenCheckedError is a common runtime error in Angular. It occurs when a component’s data changes after Angular has already checked the view in a single change detection cycle. This usually happens in development mode and can be tricky to debug in large applications. Understanding why it happens and the strategies to fix it is essential for stable Angular apps.


1. Why This Error Happens

Angular runs change detection after a component is initialized. If a bound value changes during or after this detection:

  • Angular detects that the value has changed since the last check
  • It throws the ExpressionChangedAfterItHasBeenCheckedError to notify you

Common causes include:

  • Updating component properties inside ngAfterViewInit or ngAfterContentInit
  • Changing inputs passed from a parent component after initialization
  • Modifying template-bound variables inside asynchronous callbacks like setTimeout, Promise.then(), or HTTP calls

Example scenario:

ngAfterViewInit() {
  this.title = 'New Title'; // triggers the error
}

2. Fix Using ChangeDetectorRef

Inject ChangeDetectorRef to safely trigger a new detection cycle:

import { ChangeDetectorRef, AfterViewInit } from '@angular/core';

export class ExampleComponent implements AfterViewInit {
  title = 'Initial Title';

  constructor(private cdr: ChangeDetectorRef) {}

  ngAfterViewInit() {
    this.title = 'Updated Title';
    this.cdr.detectChanges(); // Fixes the error
  }
}
  • This ensures Angular knows about the change and updates the view without throwing an error.

3. Wrap Updates in setTimeout

  • Delay property updates to the next JavaScript macro-task:
ngAfterViewInit() {
  setTimeout(() => {
    this.title = 'Updated Title';
  });
}
  • Simple and effective, but use sparingly for predictable behavior.

4. Avoid Mutating Inputs in Child Components

  • Never change an @Input property directly inside a child component.
  • Use @Output to notify the parent of changes:
@Output() valueChange = new EventEmitter<string>();

updateValue(newVal: string) {
  this.valueChange.emit(newVal);
}
  • This avoids triggering unexpected change detection cycles in Angular.

5. Handle Input Changes with ngOnChanges

  • Use ngOnChanges to detect input changes safely:
ngOnChanges(changes: SimpleChanges) {
  if (changes['inputData']) {
    this.data = changes['inputData'].currentValue;
  }
}
  • Ensures property updates happen before Angular performs template rendering, preventing the error.

6. Use AfterContentChecked and AfterViewChecked Wisely

  • Avoid updating template-bound values inside these lifecycle hooks.
  • If updates are necessary, combine with ChangeDetectorRef.detectChanges() to safely notify Angular of changes.

7. Debugging Tips

  • Check the console: Angular usually prints which expression caused the error.
  • Use ngDevMode flag: Helps to debug in development mode.
  • Review async operations: Look for setTimeout, Promise.then(), or HTTP responses that update properties.

8. Best Practices Summary

  • Avoid updating template-bound variables after Angular’s change detection.
  • Prefer ChangeDetectorRef or setTimeout when updating in lifecycle hooks.
  • Don’t mutate @Input properties inside child components.
  • Use ngOnChanges for handling input changes.
  • Profile and test components after complex updates to prevent errors.

By applying these strategies, you ensure predictable change detection, prevent runtime errors, and maintain a stable Angular application.


Citations

Internal: https://savanka.com/category/savanka-helps/
External: http://angular.dev/


Leave a Comment

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

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