Dans l'écosystème Angular, un composant traverse un cycle de vie bien défini, de sa création à sa detsruction. Chaque étape est marquée par des points d'ancrage, appelés "hooks", permettant aux développeurs d'intervenir précisément sur le comportement de l'application. Comprendre ces mécanismes est fondamental pour optimiser les performances et garantir la stabilité du code.
Tableau récapitulatif des hooks de cycle de vie
| Hook | Moment de déclenchement | Fréquence | Usage principal |
|---|---|---|---|
ngOnChanges |
Après la modification d'une propriété @Input |
Multiple | Réagir aux changements de données entrantes. |
ngOnInit |
Une fois les propriétés d'entrée initialisées | Unique | Initialisation des données et appels API. |
ngDoCheck |
Lors de chaque cycle de détection de changement | Fréquent | Détection personnalisée de modifications complexes. |
ngAfterContentInit |
Après l'insertion du contenu externe (ng-content) | Unique | Interagir avec le contenu projeté. |
ngAfterViewInit |
Après l'initialisation de la vue du composant et de ses enfants | Unique | Manipulation du DOM ou intégration de bibliothèques tierces. |
ngOnDestroy |
Juste avant la suppression du composant | Unique | Nettoyage (désabonnements, timers). |
Exploration détaillée et exemples pratiques
1. ngOnChanges : Surveillance des entrées
Ce hook reçoit un objet SimpleChanges contenant les valeurs actuelles et précédentes des propriétés décorées par @Input.
import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
@Component({
selector: 'app-fiche-article',
standalone: true,
template: `
<div>
<h2>{{ titre }}</h2>
<p>Prix : {{ prix | currency:'EUR' }}</p>
</div>
`
})
export class FicheArticleComponent implements OnChanges {
@Input() idReference!: string;
@Input() titre: string = '';
@Input() prix: number = 0;
ngOnChanges(changes: SimpleChanges) {
if (changes['idReference']) {
const refActuelle = changes['idReference'].currentValue;
const refPrecedente = changes['idReference'].previousValue;
if (!changes['idReference'].firstChange && refActuelle !== refPrecedente) {
this.actualiserDonnees(refActuelle);
}
}
}
private actualiserDonnees(id: string) {
console.log(`Mise à jour pour la référence : ${id}`);
}
}
2. ngOnInit : Le point de départ
C'est l'endroit idéal pour charger les données initiales. Contrairement au constructeur, ngOnInit garantit que les entrées (Inputs) sont disponibles.
import { Component, OnInit, Input } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'app-profil-utilisateur',
standalone: true,
template: `<p>Utilisateur : {{ profil?.nom }}</p>`
})
export class ProfilUtilisateurComponent implements OnInit {
@Input() identifiant!: number;
profil: any;
constructor(private api: HttpClient) {}
ngOnInit() {
this.api.get(`https://api.example.com/users/${this.identifiant}`)
.subscribe(res => this.profil = res);
}
}
3. ngAfterViewInit : Accès au DOM
Ce hook intervient lorsque le template du composant est entièrement rendu. Il est souvent utilisé pour initialiser des graphiques ou des plugins nécessitant un élément HTML réel.
import { Component, AfterViewInit, ViewChild, ElementRef } from '@angular/core';
@Component({
selector: 'app-zone-dessin',
standalone: true,
template: `<canvas #canevasPrincipal></canvas>`
})
export class ZoneDessinComponent implements AfterViewInit {
@ViewChild('canevasPrincipal') canevasRef!: ElementRef<HTMLCanvasElement>;
ngAfterViewInit() {
const contexte = this.canevasRef.nativeElement.getContext('2d');
if (contexte) {
contexte.fillStyle = 'red';
contexte.fillRect(10, 10, 100, 100);
}
}
}
4. ngOnDestroy : Prévention des fuites de mémoire
Essentiel pour libérer les ressources. Si vous utilisez des Observables ou des écouteurs d'événements globaux, vous devez les nettoyer ici.
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subscription, timer } from 'rxjs';
@Component({
selector: 'app-compteur-temps',
standalone: true,
template: `<p>Secondes écoulées : {{ secondes }}</p>`
})
export class CompteurTempsComponent implements OnInit, OnDestroy {
secondes = 0;
private fluxSoutenu?: Subscription;
ngOnInit() {
this.fluxSoutenu = timer(0, 1000).subscribe(val => this.secondes = val);
}
ngOnDestroy() {
// Rupture de l'abonnement pour éviter que le timer continue en arrière-plan
this.fluxSoutenu?.unsubscribe();
console.log('Ressources libérées avec succès.');
}
}
Ordre d'exécution chronologique
Lors de l'instanciation d'un composant, Angular suit cet ordre strict :
- ngOnChanges (si des entrées existent)
- ngOnInit
- ngDoCheck
- ngAfterContentInit
- ngAfterContentChecked
- ngAfterViewInit
- ngAfterViewChecked
Cycles de vie et Angular Signals
Avec l'introduction des Signals dans les versions récentes d'Angular, certains usages de ngDoCheck ou ngOnChanges peuvent être remplacés par la fonction effect() ou des signaux calculés (computed). Toutefois, les hooks traditionnels restent indispensables pour la gestion du cycle de vie du DOM et la destruction des ressources externes non réactives.