Skip to content

How to Fix Angular ExpressionChangedAfterItHasBeenCheckedError

DodaTech Updated 2026-06-24 3 min read

In this tutorial, you'll learn about How to Fix Angular ExpressionChangedAfterItHasBeenCheckedError. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

The Problem

Angular throws ExpressionChangedAfterItHasBeenCheckedError when a property changes after Angular has finished checking the view in development mode, causing a discrepancy between the current and previous value.

Quick Fix

Step 1: Move state initialization to ngOnInit

Updating shared state in child component constructors can trigger this:

@Component({
    selector: 'app-child',
    template: '{{ value }}'
})
export class ChildComponent {
    @Input() value: string;
    constructor(private service: DataService) {
        this.service.data$.subscribe(data => this.value = data);
    }
}

Move the subscription to ngOnInit:

export class ChildComponent implements OnInit {
    @Input() value: string;
    constructor(private service: DataService) {}
    ngOnInit() {
        this.service.data$.subscribe(data => this.value = data);
    }
}

Step 2: Use setTimeout for async updates

Wrap the change in setTimeout to run after change detection:

export class AppComponent {
    loading = true;
    constructor(private service: DataService) {
        this.service.load().then(() => {
            this.loading = false;
        });
    }
}

Delay the change to after change detection:

ngAfterViewInit() {
    setTimeout(() => {
        this.service.load().then(() => {
            this.loading = false;
        });
    });
}

Step 3: Use ChangeDetectionStrategy.OnPush

OnPush reduces the likelihood of expression changes:

@Component({
    selector: 'app-example',
    template: '{{ data }}',
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class ExampleComponent {
    @Input() data: string;
}

With OnPush, the component only checks for changes when inputs change or events fire.

Step 4: Use detectChanges from ChangeDetectorRef

Manually trigger change detection after the change:

export class AppComponent implements AfterViewInit {
    constructor(private cd: ChangeDetectorRef) {}
    ngAfterViewInit() {
        this.service.load().then(data => {
            this.data = data;
            this.cd.detectChanges();
        });
    }
}

Step 5: Split the component when the tree is complex

Deep nested components triggering parent updates cause this error. Refactor to pass data down rather than updating parent state from children.

Prevention

  • Initialize data in ngOnInit rather than constructors.
  • Use setTimeout to defer updates past the current change detection cycle.
  • Use ChangeDetectionStrategy.OnPush for predictable change detection.
  • Avoid having child components modify parent state during view checking.
  • Use detectChanges() explicitly when updating after AfterViewInit.

Common Mistakes with expression changed

  1. Non-exhaustive pattern matches that compile with warnings then crash at runtime
  2. Misunderstanding that String is [Char] with poor performance for large text operations
  3. Using foldl instead of foldl' causing stack overflow on large lists

These mistakes appear frequently in real-world Angular code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.

Practice Exercise

Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.

This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.

FAQ

### Why does this error only appear in development mode?

Angular runs an extra change detection pass in development mode to detect this error. The error does not appear in production mode, but the underlying timing issue still exists and can cause the view to be out of sync with the data. Fix the root cause rather than disabling dev mode.

Is this error harmful in production?

The error itself does not appear in production, but the underlying Race Condition persists. The view may display incorrect values until the next change detection cycle. In rare cases, it can lead to inconsistent UI state. Always fix the root cause.

How does OnPush change detection prevent this error?

OnPush components only check for changes when their input properties change, the component fires an event, an observable linked to the async pipe emits, or detectChanges() is called manually. This reduces the number of checks and the likelihood of detecting an in-progress mutation.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro