Guide de formation experte : maîtriser les Signaux Angular
Les Signaux sont la nouvelle primitive réactive introduite dans les dernières versions d’Angular. Cette formation est destinée aux développeurs maîtrisant déjà Angular mais n’ayant aucune connaissance des signaux. L’objectif : comprendre leur valeur, leur cycle de vie et apprendre à les utiliser dans des applications professionnelles jusqu’à la migration complète.
1. Le problème que les Signaux résolvent
Limitations du système traditionnel de détection de changements
Avant les signaux, Angular reposait principalement sur Zone.js pour sa détection automatique des changements. Zone.js intercepte chaque événement asynchrone (appels HTTP, timers, clics…) pour déclencher une vérification globale du DOM. Ce mécanisme fonctionne, mais présente des limites majeures :
- Performance : lors d’un changement, Angular vérifie tous les composants, même ceux non modifiés. Les applications complexes souffrent de cycles de détection répétés et inutiles.
- Complexité : la logique asynchrone et la gestion des souscriptions ou des EventEmitters/RxJS complexifie le code.
- Débogage difficile : Zone.js allonge les stacks d’appel et rend le parcours des erreurs ardu.
- Patchs incomplets : certaines nouvelles API JS (comme async/await) ne sont pas bien supportées, forçant des contournements.
- Gestion d’état suboptimale : Observables et Services introduisent souvent du couplage excessif ou requièrent du code spécifique pour chaque flux de données.
Pourquoi Angular a introduit les Signaux
Les signaux apportent une réactivité fine et suppriment la nécessité de scruter globalement le DOM. Quand une valeur change, seuls les composants qui dépendent de son signal sont notifiés et re-rendus. Cela permet :
- Optimisation des changements localisés : aucune propagation inutile, seul le composant concerné.
- Suppression de Zone.js et mode “zoneless” (applications plus légères et rapides).
- API simple et prévisible pour suivre, transformer ou dériver un état réactif, sans boilerplate ni gestion manuelle des souscriptions.
2. Concept des Signaux dans Angular
Définition : Qu’est-ce qu’un signal ?
Un signal est une primitive réactive — un “container intelligent” qui stocke une valeur et avertit le framework (et ses consommateurs) lorsqu’elle change. On peut :
- Créer un signal avec une valeur initiale.
- Lire la valeur en le “appelant” comme une fonction.
- Mettre à jour la valeur avec
.set()ou.update().
Comparaison aux Observables et EventEmitters
| Signal | Observable (RxJS) | EventEmitter | |
|---|---|---|---|
| Modèle réactif | Pull / synchronisé | Push / asynchronisé | Push / asynchronisé |
| Détection changements | Automatique, ciblée | Nécessite async pipe / subscription | Nécessite écoute d’événements |
| Gestion ressources | Sans nettoyage, pas de fuite | Risk de fuite mémoire | Peut nécessiter du cleanup |
| Souscription | Transparente, implicite | Explicite, subdivisée | Explicite |
| API | Simple, synchronisée | Riche, complexe | Simple, éphémère |
Cycle de vie et mécanisme interne
- Quand un signal est lu dans un contexte réactif (template, computed, effect), Angular crée un lien de dépendance.
- Lorsqu’un signal source change, Angular ne recalcule que les signaux/composants dépendants, via un graphe à dépendances (DAG).
- Les computed signals (signaux dérivés) sont recalculés à la demande ("lazy eval") pour plus d’efficacité.
- Les effects synchronisent la réactivité avec le monde extérieur (logs, DOM, APIs…), déclenchés lors des changements.
Diagrammes clés
Graphe de dépendance des signaux
Cycle de détection Zone.js vs Signaux
Cycle de vie d’un signal Angular
3. Exemples pratiques
Création et manipulation de signaux
Créer un signal
import { signal } from '@angular/core';
const count = signal(0); // Signal avec valeur initiale
Lire la valeur
console.log(count()); // 0
Mettre à jour
count.set(5);
count.update(v => v + 1);
Signaux dérivés (computed)
import { computed, signal } from '@angular/core';
const counter = signal(3);
const doubleCounter = computed(() => counter() * 2);
console.log(doubleCounter()); // 6
Quand counter change, doubleCounter est recalculé automatiquement.
Effets (effects)
Utilisez effect() pour synchroniser le changement réactif avec des traitements extérieurs :
import { signal, effect } from '@angular/core';
const name = signal('Alice');
effect(() => {
console.log('Nom actuel :', name());
});
Le log se déclenche à chaque modification de name.
Utilisation dans composants/services Angular
@Component({
selector: 'counter',
template: `
<h1>Compteur</h1>
<p>Valeur : {{ count() }}</p>
<button (click)="increment()">+1</button>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class CounterComponent {
count = signal(0);
increment() { this.count.update(c => c + 1); }
}
Aucun besoin d’appeler markForCheck(), l’intégration est automatique avec OnPush.
4. Guide de migration : de l’Angular traditionnel vers les signaux
Remplacement des @Input/@Output
Avant :
@Component({
selector: 'user-card',
template: `<p>{{ name }}</p>`
})
export class UserCardComponent {
@Input() name!: string;
@Output() onClick = new EventEmitter<MouseEvent>();
}
Après (avec signaux) :
import { input, output } from '@angular/core';
@Component({
selector: 'user-card',
template: `<p>{{ name() }}</p>`
})
export class UserCardComponent {
name = input.required<string>();
onClick = output<MouseEvent>();
}
Utilisez input() et output(), qui sont totalement compatibles avec les signaux, la composition réactive, et l’absence de décorateurs.
Migration du state BehaviorSubject et Observable
Avant :
const count$ = new BehaviorSubject(0);
count$.subscribe(value => console.log(value));
count$.next(count$.value + 1);
Après (signal) :
const count = signal(0);
console.log(count()); // 0
count.set(count() + 1); // 1
Pour bénéficier des opérateurs RxJS (debaseTime, filter…), utilisez
import { toObservable } from '@angular/core';
toObservable(count).pipe(/* opérateurs RxJS */).subscribe(...);
Et vice versa avec toSignal() pour migrer de RxJS vers signal.
Communication inter-composants
- Utilisez les signaux pour les entrées/sorties de données, transformez les services pour stocker l’état réactif dans des signaux.
- Pour les flux complexes, gardez RxJS et migrez progressivement ce qui peut l’être vers les signaux.
- Les effets synchronisent signal et DOM/monde extérieur sans abonnement manuel.
Optimisation de la détection de changement
- Mode zoneless (sans Zone.js) : supprimez la dépendance, activez la granularité maximale ; Angular programme la détection seulement pour les vues marquées sales par modification de signaux ou événements.
- Les composants OnPush sont nativement optimisés, et le code se simplifie drastiquement.
Stratégie de migration
5. Synthèse
- Les signaux sont la nouvelle référence pour la réactivité locale et la gestion d’état en Angular. Favorisez-les pour toute nouvelle application ou migration incrémentale.
- RxJS reste utile pour les flux asynchrones complexes ou interopérabilité avec des APIs externes, mais évitez de l’utiliser pour du simple état local ou la communication parent/enfant.
- Remplacez Zone.js si possible par le mode zoneless et tirez parti des performances maximales.
- Utilisez les diagrammes de dépendance pour bien comprendre et visualiser votre architecture réactive.
- Attention aux effets : employez-les principalement pour interfacer signal et logique non-réactive (side effects), pas pour des calculs ou du business logic complexe, et évitez les boucles infinies.