Overview
Angular’s change detection system is responsible for synchronizing your application’s data model with its view. Understanding how it works is crucial for building performant applications.
Change Detection Strategies
Angular provides two primary change detection strategies that control when and how components are checked for changes.
Default (Eager) Strategy
The Default strategy (also known as Eager) checks the component eagerly during every change detection cycle.
import { Component , ChangeDetectionStrategy } from '@angular/core' ;
@ Component ({
selector: 'app-eager' ,
template: `
<h2>{{ title }}</h2>
<p>Checked: {{ getCheckedCount() }}</p>
` ,
changeDetection: ChangeDetectionStrategy . Default
})
export class EagerComponent {
title = 'Eager Component' ;
private checkedCount = 0 ;
getCheckedCount () : number {
return ++ this . checkedCount ;
}
}
The Default strategy has been renamed to Eager in recent versions. Both names refer to the same behavior.
OnPush Strategy
The OnPush strategy only checks the component when:
An input property reference changes
An event originates from the component or its children
Change detection is manually triggered
An observable linked to the template emits a new value
import { Component , ChangeDetectionStrategy , Input } from '@angular/core' ;
interface User {
id : number ;
name : string ;
email : string ;
}
@ Component ({
selector: 'app-user-card' ,
template: `
<div class="card">
<h3>{{ user.name }}</h3>
<p>{{ user.email }}</p>
<button (click)="onEdit()">Edit</button>
</div>
` ,
changeDetection: ChangeDetectionStrategy . OnPush
})
export class UserCardComponent {
@ Input () user !: User ;
onEdit () : void {
// Event handler triggers change detection
console . log ( 'Editing user:' , this . user . id );
}
}
Manual Change Detection
ChangeDetectorRef
Use ChangeDetectorRef to manually control change detection in your components.
import {
Component ,
ChangeDetectionStrategy ,
ChangeDetectorRef ,
OnInit ,
OnDestroy
} from '@angular/core' ;
import { interval , Subscription } from 'rxjs' ;
@ Component ({
selector: 'app-manual-cd' ,
template: `
<div class="status">
<h3>Real-time Data</h3>
<p>Counter: {{ counter }}</p>
<p>Last Updated: {{ lastUpdate | date:'medium' }}</p>
</div>
` ,
changeDetection: ChangeDetectionStrategy . OnPush
})
export class ManualChangeDetectionComponent implements OnInit , OnDestroy {
counter = 0 ;
lastUpdate = new Date ();
private subscription ?: Subscription ;
constructor ( private cdr : ChangeDetectorRef ) {}
ngOnInit () : void {
// Update data outside Angular's zone
this . subscription = interval ( 1000 ). subscribe (() => {
this . counter ++ ;
this . lastUpdate = new Date ();
// Manually trigger change detection
this . cdr . markForCheck ();
});
}
ngOnDestroy () : void {
this . subscription ?. unsubscribe ();
}
}
Key Methods
markForCheck()
detectChanges()
detach()
reattach()
// Marks the component and its ancestors for checking
// Use with OnPush strategy
this . cdr . markForCheck ();
Immutable Data Patterns
When using OnPush, work with immutable data to ensure change detection triggers correctly.
import { Component , ChangeDetectionStrategy } from '@angular/core' ;
interface TodoItem {
id : number ;
title : string ;
completed : boolean ;
}
@ Component ({
selector: 'app-todo-list' ,
template: `
<div *ngFor="let todo of todos; trackBy: trackByFn">
<app-todo-item
[todo]="todo"
(toggle)="onToggle($event)"
></app-todo-item>
</div>
` ,
changeDetection: ChangeDetectionStrategy . OnPush
})
export class TodoListComponent {
todos : TodoItem [] = [
{ id: 1 , title: 'Learn Angular' , completed: false },
{ id: 2 , title: 'Build an app' , completed: false }
];
onToggle ( id : number ) : void {
// Create new array reference for OnPush to detect change
this . todos = this . todos . map ( todo =>
todo . id === id
? { ... todo , completed: ! todo . completed }
: todo
);
}
trackByFn ( index : number , item : TodoItem ) : number {
return item . id ;
}
}
Avoiding Common Pitfalls
Avoid calling functions or methods directly in templates with OnPush strategy, as they may not be re-evaluated when expected.
// ❌ Bad: Function calls in template
@ Component ({
template: `<p>{{ getFormattedDate() }}</p>` ,
changeDetection: ChangeDetectionStrategy . OnPush
})
export class BadComponent {
getFormattedDate () : string {
return new Date (). toISOString ();
}
}
// ✅ Good: Use pipes or properties
@ Component ({
template: `<p>{{ currentDate | date:'medium' }}</p>` ,
changeDetection: ChangeDetectionStrategy . OnPush
})
export class GoodComponent {
currentDate = new Date ();
}
Change Detection with Signals
When using signals in your templates, Angular automatically tracks dependencies and triggers change detection efficiently.
import { Component , signal , computed , ChangeDetectionStrategy } from '@angular/core' ;
@ Component ({
selector: 'app-counter' ,
template: `
<div class="counter">
<p>Count: {{ count() }}</p>
<p>Double: {{ doubled() }}</p>
<button (click)="increment()">Increment</button>
</div>
` ,
changeDetection: ChangeDetectionStrategy . OnPush
})
export class CounterComponent {
count = signal ( 0 );
doubled = computed (() => this . count () * 2 );
increment () : void {
this . count . update ( n => n + 1 );
}
}
Signals work seamlessly with OnPush change detection, automatically marking components for check when signal values change.
Zone.js Integration
Angular uses Zone.js to automatically detect asynchronous operations and trigger change detection.
import { Component , NgZone } from '@angular/core' ;
@ Component ({
selector: 'app-zone-aware' ,
template: `<p>Data: {{ data }}</p>`
})
export class ZoneAwareComponent {
data = 'initial' ;
constructor ( private ngZone : NgZone ) {
// Run code outside Angular's zone for performance
this . ngZone . runOutsideAngular (() => {
setInterval (() => {
// Update data outside zone
const newData = this . fetchData ();
// Run change detection when needed
this . ngZone . run (() => {
this . data = newData ;
});
}, 5000 );
});
}
private fetchData () : string {
return `Updated at ${ new Date (). toLocaleTimeString () } ` ;
}
}
Best Practices
Use OnPush Set OnPush strategy on presentational components to reduce unnecessary checks.
Immutable Data Use immutable data patterns to make change detection reliable and predictable.
TrackBy Functions Always use trackBy with *ngFor to minimize DOM operations.
Avoid Heavy Computations Move expensive calculations to pipes or pre-compute them in component logic.
Additional Resources