Appshref
Programming / Software / AI
Published on: Feb 2, 2025, in

Understanding the "Expression Changed After Checked" Error in Angular

Understanding the

Introduction

Angular is a powerful framework for building dynamic web applications. However, developers often encounter an error known as "ExpressionChangedAfterItHasBeenCheckedError". This error can be confusing, especially for those new to Angular’s change detection mechanism.

In this article, we will explore:

  • What this error is
  • Why it occurs
  • How to prevent it
  • How to fix it effectively

By the end of this article, you will have a solid understanding of how to avoid this issue and ensure smoother Angular development.

What is "Expression Changed After Checked" Error?

The ExpressionChangedAfterItHasBeenCheckedError occurs when Angular detects that a binding value has changed after change detection has already run. Angular has a mechanism to verify that bindings remain stable after the initial change detection cycle. If a value changes unexpectedly, Angular throws this error.

Example Error Message

ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.
Previous value: 'false'. Current value: 'true'.

This error often happens in development mode because Angular runs change detection twice to ensure values do not change unexpectedly.

Why Does This Error Occur?

This error occurs due to how Angular handles change detection. Some common scenarios that trigger it include:

1. Changing a Value Inside ngAfterViewInit or ngAfterContentInit

Lifecycle hooks like ngAfterViewInit and ngAfterContentInit run after the first change detection cycle. If you modify a bound property inside these hooks, it triggers this error.

Example:

@Component({
  selector: "app-example",
  template: `<p>{{ value }}</p>`,
})
export class ExampleComponent {
  value = false;

  ngAfterViewInit() {
    this.value = true; // This will cause the error
  }
}

2. Updating Values Inside an async Operation

If an async operation like setTimeout or an HTTP request updates a value after the first change detection cycle, the error occurs.

Example:

ngOnInit() {
  setTimeout(() => {
    this.value = true;
  }, 0);
}

3. Direct DOM Manipulations Affecting Bindings

Using ElementRef or Renderer2 to modify the DOM can cause this error because Angular is not aware of these changes.

How to Avoid the Error?

There are several strategies to prevent this error from occurring:

1. Use setTimeout or Promise.resolve()

Wrapping the update inside setTimeout or Promise.resolve() defers the change to the next JavaScript event loop cycle.

Example:

ngAfterViewInit() {
  setTimeout(() => {
    this.value = true;
  });
}

2. Use ChangeDetectorRef.detectChanges()

Injecting ChangeDetectorRef and calling detectChanges() forces Angular to check for changes manually.

Example:

constructor(private cdr: ChangeDetectorRef) {}

ngAfterViewInit() {
  this.value = true;
  this.cdr.detectChanges();
}

3. Mark Component for Change with ChangeDetectorRef.markForCheck()

If your component uses OnPush change detection, call markForCheck() to schedule a change detection cycle.

Example:

constructor(private cdr: ChangeDetectorRef) {}

ngAfterViewInit() {
  this.value = true;
  this.cdr.markForCheck();
}

4. Use BehaviorSubject or EventEmitter for State Updates

Using BehaviorSubject (from RxJS) or EventEmitter ensures updates are properly handled in an observable manner.

Example:

value$ = new BehaviorSubject<boolean>(false);

ngAfterViewInit() {
  this.value$.next(true);
}

5. Avoid Binding Expressions That Change During Change Detection

Avoid expressions in templates that modify values directly. Instead, compute values in the component class.

Bad Example:

<p>{{ Math.random() }}</p>

Good Example:

randomValue = Math.random();
<p>{{ randomValue }}</p>

How to Fix the Error?

If you encounter this error, try the following approaches:

1. Identify Where the Change Happens

Check the error message to see which property has changed. Locate the lifecycle hook or function where the update occurs.

2. Use setTimeout or Promise.resolve()

If the change happens in ngAfterViewInit, wrap it inside setTimeout() or Promise.resolve().

3. Use ChangeDetectorRef.detectChanges()

If necessary, force Angular to check for updates manually by injecting ChangeDetectorRef.

4. Reconsider Component Structure

If changes are happening unexpectedly, consider restructuring the component. Move state updates to ngOnInit() instead of ngAfterViewInit().

Conclusion

The "ExpressionChangedAfterItHasBeenCheckedError" occurs when Angular detects that a bound value has changed unexpectedly after the initial change detection cycle. This happens due to lifecycle hooks, async operations, or direct DOM manipulations.

To prevent and fix this error:

  • Use setTimeout or Promise.resolve() to defer updates
  • Use ChangeDetectorRef.detectChanges() if necessary
  • Avoid binding expressions that change during rendering
  • Leverage RxJS subjects for state updates

By following these best practices, you can prevent this error and ensure a smoother Angular development experience.