Angular’s ExpressionChangedAfterItHasBeenCheckedError is one of the most confusing runtime errors developers face. It usually appears during development (not production) and indicates that a value in your component changed after Angular completed its change detection cycle.
This issue often affects forms, async data, child components, or values updated inside lifecycle hooks.
This guide explains why it happens, how to fix it, and best practices to prevent it.
1. Why Does This Error Happen?
Angular checks your component state twice in development mode:
- After rendering
- After verifying the state hasn’t changed unexpectedly
If Angular finds that a variable changed in between these checks, it throws:
ExpressionChangedAfterItHasBeenCheckedError
This mainly occurs when:
- You update DOM-bound values inside
ngAfterViewInit - You modify a value inside a child component unexpectedly
- Async operations update values during the wrong lifecycle timing
- Change detection runs earlier or later than expected
2. Common Scenarios That Cause This Error
Scenario 1: Updating a Bound Variable in ngAfterViewInit
Example:
ngAfterViewInit() {
this.title = 'Updated Title';
}
Scenario 2: Async Data Changing the UI Before Angular Stabilizes
Example:
setTimeout(() => {
this.count++;
});
Scenario 3: Child Component Emits Too Early
Example:
@Output() dataChanged = new EventEmitter();
ngAfterViewInit() {
this.dataChanged.emit(); // too early
}
3. How to Fix the Error
Fix 1: Move Logic to ngOnInit Instead of ngAfterViewInit
Bad:
ngAfterViewInit() {
this.value = true;
}
Good:
ngOnInit() {
this.value = true;
}
Use ngAfterViewInit only for DOM-related logic.
Fix 2: Wrap Async Updates in ChangeDetectorRef
If async calls run before Angular stabilizes:
constructor(private cdr: ChangeDetectorRef) {}
ngAfterViewInit() {
setTimeout(() => {
this.value = true;
this.cdr.detectChanges();
});
}
This manually triggers detection at the correct time.
Fix 3: Use setTimeout() Without ChangeDetectorRef (Simplest Fix)
This defers updates to the next JavaScript macrotask cycle.
ngAfterViewInit() {
setTimeout(() => {
this.value = true;
});
}
Though simple, it works for many UI-update cases.
Fix 4: Use ngZone.run() for manual change detection
If updates happen outside Angular’s zone:
constructor(private ngZone: NgZone) {}
someExternalCallback() {
this.ngZone.run(() => {
this.isReady = true;
});
}
Fix 5: For Child Components — Emit Events After Initialization
Bad:
ngAfterViewInit() {
this.dataChanged.emit(true);
}
Good:
ngOnInit() {
queueMicrotask(() => this.dataChanged.emit(true));
}
4. Best Practices to Avoid This Error
- Avoid modifying data in
ngAfterViewInit - Prefer reactive forms instead of heavy template bindings
- Stabilize async operations before updating UI
- Avoid deeply nested child component outputs
- Use
OnPushchange detection for predictable state updates - Avoid two-way binding for complex structures
5. Quick Debug Checklist
If you see this error:
- Identify which variable changed unexpectedly (Angular console log shows it).
- Check where the variable is updated (lifecycle hook or async call).
- Move the update to a stable hook (
ngOnInit, notngAfterViewInit). - If async, use
setTimeout()orChangeDetectorRef. - Confirm that child components aren’t emitting too early.
Citations
Internal: https://savanka.com/category/savanka-helps/
External: http://angular.dev/