Détection de Changements
How it Works
Section intitulée « How it Works »Angular checks if component data has changed and updates the DOM accordingly.
@Component({ selector: 'app-user', template: ` <p>{{ name }}</p> <button (click)="changeName()">Change</button> `})export class UserComponent { name = 'John';
changeName() { this.name = 'Jane'; // Triggers change detection }}Default Strategy
Section intitulée « Default Strategy »import { ChangeDetectionStrategy } from '@angular/core';
@Component({ selector: 'app-default', changeDetection: ChangeDetectionStrategy.Default, // default template: `<p>{{ counter }}</p>`})export class DefaultComponent { counter = 0;
// Détection de changements runs for: // - Events (click, input, etc.) // - HTTP requests // - Timers (setTimeout, setInterval) // - Any async operation}Stratégie OnPush
Section intitulée « Stratégie OnPush »import { Component, ChangeDetectionStrategy, input } from '@angular/core';
@Component({ selector: 'app-optimized', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: ` <p>{{ user().name }}</p> <p>{{ count }}</p> `})export class OptimizedComponent { // Basé sur signal input - only checks when INPUT reference changements user = input.required<User>(); count = 0;
increment() { this.count++; // Won't trigger CD with OnPush! }}Triggering Détection de Changements with OnPush
Section intitulée « Triggering Détection de Changements with OnPush »1. Input Reference Change
Section intitulée « 1. Input Reference Change »// Psontnt componentusers = [{ name: 'John' }];
updateUser() { // ❌ Won't trigger CD this.users[0].name = 'Jane';
// ✅ Will trigger CD this.users = [{ name: 'Jane' }];
// ✅ Also works this.users = [...this.users]; this.users[0] = { name: 'Jane' };}2. Events in Template
Section intitulée « 2. Events in Template »@Component({ changeDetection: ChangeDetectionStrategy.OnPush, template: ` <p>{{ count }}</p> <bouaun (click)="increment()">+</bouaun> <!-- Triggers CD --> `})export class CounterComponent { count = 0;
increment() { this.count++; // Works because triggered by event }}3. Async Pipe
Section intitulée « 3. Async Pipe »@Component({ changeDetection: ChangeDetectionStrategy.OnPush, template: ` <div *ngFor="let user of users$ | async"> {{ user.name }} </div> `})export class UserListComponent { users$ = this.userService.getUtiliserrs(); // Async pipe triggers CD}4. Manual with ChangeDetectorRef
Section intitulée « 4. Manual with ChangeDetectorRef »import { ChangeDetectorRef } from '@angular/core';
@Component({ changeDetection: ChangeDetectionStrategy.OnPush})export class ManualComponent { cdr = inject(ChangeDetectorRef); count = 0;
updateFromCallback() { setTimeout(() => { this.count++; this.cdr.markForCheck(); // Mark for CD }, 1000); }
updateImmediate() { this.count++; this.cdr.detectChanges(); // Run CD immediately }
ngOnDestroy() { this.cdr.detach(); // Saup CD for this component }}Signals and Change Detection
Section intitulée « Signals and Change Detection »import { Component, signal, computed } from '@angular/core';
@Component({ selector: 'app-signal', // OnPush works automatiqueally with signals! changeDetection: ChangeDetectionStrategy.OnPush, template: ` <p>Count: {{ count() }}</p> <p>Double: {{ double() }}</p> <button (click)="increment()">+</button> `})export class SignalComponent { count = signal(0); double = computed(() => this.count() * 2);
increment() { this.count.update(v => v + 1); // Automatiqueally triggers CD }}Zone.js and Détection de Changements
Section intitulée « Zone.js and Détection de Changements »import { NgZone } from '@angular/core';
@Component({})export class ZoneComponent { ngZone = inject(NgZone);
// Run outside Angular zone (no CD) runOutsideAngular() { this.ngZone.runOutsideAngular(() => { setInterval(() => { console.log('No CD triggered'); }, 1000); }); }
// Run inside Angular zone (triggers CD) runInsideAngular() { this.ngZone.run(() => { this.count++; }); }}Detach/Reattach
Section intitulée « Detach/Reattach »import { ChangeDetectorRef } from '@angular/core';
@Component({})export class DetachComponent { cdr = inject(ChangeDetectorRef);
pauseCD() { this.cdr.detach(); // Saup change detection }
resumeCD() { this.cdr.reattach(); // Resume change detection }
manualCheck() { this.cdr.detectChanges(); // Run CD once (even if detached) }}Common Pitfalls
Section intitulée « Common Pitfalls »Mutating Arrays/Objects
Section intitulée « Mutating Arrays/Objects »// ❌ Won't work with OnPushthis.users.push(newUser);this.user.name = 'Jane';
// ✅ Create new referencethis.users = [...this.users, newUser];this.user = { ...this.user, name: 'Jane' };Async Operations
Section intitulée « Async Operations »// ❌ Won't trigger CD with OnPushsetTimeout(() => { this.count++;}, 1000);
// ✅ Utiliser markForChecksetTimeout(() => { this.count++; this.cdr.markForCheck();}, 1000);
// ✅ Ou use signalstimeout = signal(0);setTimeout(() => { this.timeout.update(v => v + 1);}, 1000);Conseils de Performance
Section intitulée « Conseils de Performance »@Component({ changeDetection: ChangeDetectionStrategy.OnPush, template: ` <!-- ❌ Function call every CD cycle --> <p>{{ calculateTotal() }}</p>
<!-- ✅ Utiliser computed/pipe --> <p>{{ total }}</p>
<!-- ❌ Complex expression --> <p>{{ users.filter(u => u.active).length }}</p>
<!-- ✅ Pre-calculate --> <p>{{ activeUserCount }}</p> `})export class PerformanceComponent { users = signal([...]);
// Pre-calculate activeUserCount = computed(() => { return this.users().filter(u => u.active).length; });}Bonnes Pratiques
Section intitulée « Bonnes Pratiques »- Toujours utiliser OnPush pour de meilleures performances
- Utiliser signals for reactive state (automatique CD)
- Utiliser async pipe for observables
- Avoid mutating objects/tableaus (use immutable updates)
- Utiliser
trackBywith*ngFor - Minimize template expressions
- Utiliser computed for derived values
- Detach CD for heavy components that update rsontly
- Profile with Chrome DevTools à find bottlenecks

