Angular : les lifecycle hooks expliqués

🏷️ Front-end 📅 13/04/2026 12:00:00 👤 Mezgani said
Angular Hooks Lifecycle Composants
Angular : les lifecycle hooks expliqués

Maîtrisez tous les lifecycle hooks d'Angular : ngOnInit, ngOnDestroy, ngOnChanges, ngAfterViewInit et les nouveaux hooks de rendu. Guide complet avec exemples pratiques.

Vue d'ensemble des lifecycle hooks

Les lifecycle hooks sont des méthodes spéciales appelées automatiquement par Angular à des moments précis du cycle de vie d'un composant ou d'une directive. Ils permettent d'exécuter du code à l'initialisation, lors des changements, ou à la destruction du composant.

À retenir : Chaque hook correspond à une interface TypeScript à implémenter. Bien que non obligatoire en JS, déclarer implements OnInit offre une vérification de type au compile.

Liste complète des hooks :

  • ngOnChanges — déclenché quand un @Input() change (avant ngOnInit)
  • ngOnInit — une seule fois après le premier ngOnChanges
  • ngDoCheck — à chaque cycle de détection de changement
  • ngAfterContentInit — après l'insertion du contenu projeté (ng-content)
  • ngAfterContentChecked — après chaque vérification du contenu projeté
  • ngAfterViewInit — après l'initialisation de la vue et des vues enfants
  • ngAfterViewChecked — après chaque vérification de la vue
  • ngOnDestroy — juste avant la destruction du composant

ngOnInit — initialisation du composant

ngOnInit est le hook le plus utilisé. Il s'exécute une seule fois après que les propriétés @Input() ont été initialisées. C'est ici qu'on fait les appels API, les abonnements, et l'initialisation de l'état.

import { Component, OnInit } from '@angular/core';
import { UserService } from './user.service';

@Component({ selector: 'app-profile', template: '...' })
export class ProfileComponent implements OnInit {

  user: User | null = null;

  constructor(private userService: UserService) {}

  ngOnInit(): void {
    // Appel API après que les @Input() sont disponibles
    this.userService.getCurrentUser().subscribe(user => {
      this.user = user;
    });
  }
}
ngOnInit vs constructeur : Le constructeur est réservé à l'injection de dépendances. ngOnInit est le bon endroit pour la logique d'initialisation — les @Input() ne sont pas encore disponibles dans le constructeur.

ngOnChanges — réaction aux inputs

ngOnChanges est appelé avant ngOnInit et à chaque fois qu'un @Input() change. Il reçoit un objet SimpleChanges contenant les valeurs précédentes et actuelles.

import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';

@Component({ selector: 'app-chart', template: '...' })
export class ChartComponent implements OnChanges {

  @Input() data: number[] = [];
  @Input() title = '';

  ngOnChanges(changes: SimpleChanges): void {
    if (changes['data']) {
      const prev = changes['data'].previousValue;
      const curr = changes['data'].currentValue;
      const isFirst = changes['data'].firstChange;

      console.info(`data changé : ${prev} → ${curr} (premier: ${isFirst})`);
      this.redrawChart();
    }

    if (changes['title'] && !changes['title'].firstChange) {
      // Réagir uniquement aux changements suivants (pas l'init)
      this.updateTitle(changes['title'].currentValue);
    }
  }

  private redrawChart(): void { /* ... */ }
  private updateTitle(t: string): void { /* ... */ }
}
À retenir : ngOnChanges n'est déclenché que pour les @Input() primitifs ou les changements de référence d'objet. Muter un objet existant ne déclenchera pas ngOnChanges.

ngOnDestroy — nettoyage des ressources

ngOnDestroy est appelé juste avant qu'Angular détruise le composant. C'est impératif de l'utiliser pour éviter les memory leaks : désabonnements, suppression de timers, fermeture de WebSockets.

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject, interval } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

@Component({ selector: 'app-timer', template: '{{ count }}' })
export class TimerComponent implements OnInit, OnDestroy {

  count = 0;
  private destroy$ = new Subject<void>();

  ngOnInit(): void {
    // Le Subject coupe automatiquement l'abonnement à la destruction
    interval(1000).pipe(
      takeUntil(this.destroy$)
    ).subscribe(() => this.count++);
  }

  ngOnDestroy(): void {
    this.destroy$.next();
    this.destroy$.complete();
  }
}
Alternative moderne : Avec Angular 16+, utilisez takeUntilDestroyed() depuis @angular/core/rxjs-interop pour éviter le boilerplate du Subject.
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';

export class TimerComponent {
  count = 0;

  constructor() {
    interval(1000).pipe(
      takeUntilDestroyed() // se détruit automatiquement avec le composant
    ).subscribe(() => this.count++);
  }
}

ngAfterViewInit et ngAfterContentInit

Ces hooks donnent accès aux éléments du DOM et aux composants enfants après leur initialisation.

ngAfterViewInit — accès aux @ViewChild et éléments du template :

import { Component, AfterViewInit, ViewChild, ElementRef } from '@angular/core';

@Component({
  selector: 'app-canvas',
  template: '<canvas #myCanvas></canvas>'
})
export class CanvasComponent implements AfterViewInit {

  @ViewChild('myCanvas') canvas!: ElementRef<HTMLCanvasElement>;

  ngAfterViewInit(): void {
    // Le canvas est disponible ici — pas avant
    const ctx = this.canvas.nativeElement.getContext('2d');
    ctx?.fillRect(0, 0, 100, 100);
  }
}

ngAfterContentInit — accès au contenu projeté via @ContentChild :

import { Component, AfterContentInit, ContentChild } from '@angular/core';

@Component({
  selector: 'app-card',
  template: '<ng-content></ng-content>'
})
export class CardComponent implements AfterContentInit {

  @ContentChild('cardTitle') title!: ElementRef;

  ngAfterContentInit(): void {
    // Le contenu projeté est disponible ici
    console.info('Titre projeté :', this.title?.nativeElement.textContent);
  }
}
À retenir : Ne jamais modifier des données dans ngAfterViewChecked ou ngAfterContentChecked — cela provoquerait une erreur "Expression changed after check" en mode développement.

ngDoCheck — détection personnalisée

ngDoCheck est déclenché à chaque cycle de détection de changement, même si aucun @Input() n'a changé. Il permet de détecter des mutations que ngOnChanges ne voit pas (mutation d'un tableau ou d'un objet).

import { Component, DoCheck, Input, IterableDiffer, IterableDiffers } from '@angular/core';

@Component({ selector: 'app-list', template: '...' })
export class ListComponent implements DoCheck {

  @Input() items: string[] = [];
  private differ: IterableDiffer<string>;

  constructor(differs: IterableDiffers) {
    this.differ = differs.find([]).create();
  }

  ngDoCheck(): void {
    const changes = this.differ.diff(this.items);
    if (changes) {
      console.info('Tableau muté :');
      changes.forEachAddedItem(r => console.info('Ajout :', r.item));
      changes.forEachRemovedItem(r => console.info('Suppression :', r.item));
    }
  }
}
Performance : ngDoCheck est appelé très fréquemment. Gardez son implémentation aussi légère que possible, ou préférez un observable/signal pour réagir aux changements.

afterNextRender et afterRender (Angular 16+)

Angular 16 a introduit deux nouvelles fonctions de hook de rendu, distinctes du système de lifecycle classique. Elles s'exécutent dans le contexte du navigateur uniquement (pas en SSR).

afterNextRender() — s'exécute une seule fois après le prochain rendu :

import { Component, afterNextRender, ElementRef, viewChild } from '@angular/core';

@Component({ selector: 'app-chart', template: '<canvas #chart></canvas>' })
export class ChartComponent {
  private chart = viewChild<ElementRef>('chart');

  constructor() {
    afterNextRender(() => {
      // Initialisation d'une librairie JS tierce (Chart.js, D3...)
      // Exécuté une seule fois, après le premier rendu
      initChart(this.chart()?.nativeElement);
    });
  }
}

afterRender() — s'exécute après chaque rendu :

import { afterRender } from '@angular/core';

export class ScrollComponent {
  constructor() {
    afterRender(() => {
      // Mis à jour à chaque rendu — utile pour mesures DOM
      this.updateScrollPosition();
    });
  }
}
À retenir : Préférez afterNextRender() à ngAfterViewInit pour l'intégration de librairies DOM tierces dans les applications standalone. Ces hooks sont compatibles SSR et ne s'exécutent pas côté serveur.

Ordre d'exécution complet

Voici l'ordre exact dans lequel Angular appelle les hooks sur un composant :

// Ordre de création
1. constructor()
2. ngOnChanges()        ← si @Input() présents
3. ngOnInit()
4. ngDoCheck()
5. ngAfterContentInit()
6. ngAfterContentChecked()
7. ngAfterViewInit()
8. ngAfterViewChecked()

// Cycles suivants (à chaque détection de changement)
ngOnChanges()           ← si un @Input() a changé
ngDoCheck()
ngAfterContentChecked()
ngAfterViewChecked()

// Destruction
ngOnDestroy()
Conseil : Dans la pratique, 90% des cas sont couverts par 3 hooks : ngOnInit pour l'initialisation, ngOnChanges pour réagir aux inputs, et ngOnDestroy pour le nettoyage. Les autres hooks restent utiles dans des cas spécifiques.