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
ExpressionChangedAfterItHasBeenCheckedErrorto notify you
Common causes include:
- Updating component properties inside
ngAfterViewInitorngAfterContentInit - 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
@Inputproperty directly inside a child component. - Use
@Outputto 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
ngOnChangesto 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
ngDevModeflag: 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
ChangeDetectorReforsetTimeoutwhen updating in lifecycle hooks. - Don’t mutate
@Inputproperties inside child components. - Use
ngOnChangesfor 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/