How To Fix ExpressionChangedAfterItHasBeenCheckedError ?

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:

  1. After rendering
  2. 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 OnPush change detection for predictable state updates
  • Avoid two-way binding for complex structures

5. Quick Debug Checklist

If you see this error:

  1. Identify which variable changed unexpectedly (Angular console log shows it).
  2. Check where the variable is updated (lifecycle hook or async call).
  3. Move the update to a stable hook (ngOnInit, not ngAfterViewInit).
  4. If async, use setTimeout() or ChangeDetectorRef.
  5. Confirm that child components aren’t emitting too early.

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 *