Examples
✅ Good - OnPush with Immutable Data
@Component({ selector: "app-user-list", changeDetection: ChangeDetectionStrategy.OnPush, template: ` <div *ngFor="let user of users; trackBy: trackByUserId"> <app-user-card [user]="user" (userClicked)="onUserClicked($event)"> </app-user-card> </div> `,})export class UserListComponent { @Input() users: User[] = []; @Output() userSelected = new EventEmitter<User>();
trackByUserId(index: number, user: User): string { return user.id; }
onUserClicked(user: User): void { this.userSelected.emit(user); }}
✅ Good - OnPush with Signals
@Component({ selector: "app-counter", changeDetection: ChangeDetectionStrategy.OnPush, template: ` <div> <p>Count: {{ count() }}</p> <p>Double: {{ doubled() }}</p> <button (click)="increment()">+</button> <button (click)="decrement()">-</button> </div> `,})export class CounterComponent { count = signal(0); doubled = computed(() => this.count() * 2);
increment(): void { this.count.update((value) => value + 1); }
decrement(): void { this.count.update((value) => value - 1); }}
✅ Good - OnPush with Manual Change Detection
@Component({ selector: "app-data-loader", changeDetection: ChangeDetectionStrategy.OnPush, template: ` <div *ngIf="loading">Loading...</div> <div *ngIf="data && !loading"> <pre>{{ data | json }}</pre> </div> `,})export class DataLoaderComponent implements OnInit { data: any = null; loading = false;
private cdr = inject(ChangeDetectorRef); private httpClient = inject(HttpClient);
ngOnInit(): void { this.loadData(); }
private loadData(): void { this.loading = true; this.cdr.markForCheck(); // Manual trigger
this.httpClient.get("/api/data").subscribe({ next: (response) => { this.data = response; this.loading = false; this.cdr.markForCheck(); // Manual trigger }, error: () => { this.loading = false; this.cdr.markForCheck(); // Manual trigger }, }); }}
❌ Bad - Default Change Detection (Inefficient)
@Component({ selector: "app-user-list", // No changeDetection specified = Default strategy template: ` <div *ngFor="let user of users"> <app-user-card [user]="user"></app-user-card> </div> `,})export class UserListComponent { @Input() users: User[] = [];
// This will trigger change detection on every cycle // even when users array hasn't changed}
❌ Bad - OnPush with Mutable Operations
@Component({ selector: "app-todo-list", changeDetection: ChangeDetectionStrategy.OnPush, template: ` <div *ngFor="let todo of todos">{{ todo.title }} - {{ todo.completed ? "Done" : "Pending" }}</div> <button (click)="toggleFirst()">Toggle First</button> `,})export class TodoListComponent { @Input() todos: Todo[] = [];
toggleFirst(): void { // ❌ This won't trigger change detection in OnPush // because we're mutating the existing object this.todos[0].completed = !this.todos[0].completed; }}