Expression changed after digest cycle

Loading

The error “Expression changed after digest cycle” occurs when Angular detects that a binding value has changed after the initial change detection cycle has completed. This typically happens due to updates triggered asynchronously or inside Angular’s lifecycle hooks.


Step 1: What Causes This Error?

1. Updating a Model Inside a Lifecycle Hook

If you update a bound variable inside Angular’s lifecycle hooks like ngOnInit() (Angular) or $digest() (AngularJS), Angular may detect a second change after the initial one.

Example (Angular)

export class AppComponent {
message: string = 'Hello';

ngOnInit() {
this.message = 'Updated Message'; // This will cause an error
}
}

Here, message is changed after the first digest cycle, leading to an error.

Solution: Use setTimeout()

ngOnInit() {
setTimeout(() => {
this.message = 'Updated Message';
});
}

This moves the update to the next cycle, preventing the error.


2. Asynchronous Updates (Promises, Observables, setTimeout)

If data updates asynchronously but outside Angular’s change detection cycle, it can cause this error.

Example (Promise)

ngOnInit() {
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
this.message = data.text; // This may cause an error
});
}

The API updates message after the first cycle, triggering an error.

Solution: Use ChangeDetectorRef

Inject ChangeDetectorRef and use detectChanges():

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

constructor(private cd: ChangeDetectorRef) {}

ngOnInit() {
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
this.message = data.text;
this.cd.detectChanges(); // Forces Angular to detect changes safely
});
}

3. Direct DOM Manipulation

If you manually update the DOM (e.g., document.querySelector()), Angular won’t track the change, causing this error.

Example

document.getElementById('title')!.innerText = 'New Title';

Solution: Use Angular Data Binding

Instead of modifying the DOM manually, bind the value in HTML:

<h1>{{ title }}</h1>

4. Using ngModel or Form Controls Inside Lifecycle Hooks

If you update a form control inside ngOnInit(), it might trigger the error.

Example

this.form.controls['username'].setValue('newUser');  // Causes error

Solution: Use setTimeout()

setTimeout(() => {
this.form.controls['username'].setValue('newUser');
});

5. Updating Data Inside a Child Component

If a child component updates a parent component’s data, Angular detects multiple changes.

Example

@Input() message!: string;

ngOnChanges() {
this.message = 'Updated by Child';
}

Solution: Use ngAfterViewInit() Instead

ngAfterViewInit() {
setTimeout(() => {
this.message = 'Updated by Child';
});
}

Step 2: Debugging Steps

  1. Check for Updates in Lifecycle Hooks (ngOnInit(), $digest()).
  2. Ensure Asynchronous Updates (setTimeout(), Promise, Observable) Are Handled Properly.
  3. Use ChangeDetectorRef.detectChanges() When Necessary.
  4. Avoid Direct DOM Manipulation (document.querySelector()).
  5. Use ngAfterViewInit() Instead of ngOnInit() for UI Updates.

Leave a Reply

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