🎬 Passer en mode présentation

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

Signal : nameSignal : surnameComputed : fullNameEffect : Log changesComponent Template
Signal : nameSignal : surnameComputed : fullNameEffect : Log changesComponent Template

Cycle de détection Zone.js vs Signaux

User ActionZone.jsChange DetectionSignalsDOMUser ActionUser ActionZone.jsZone.jsChange DetectionChange DetectionSignalsSignalsDOMDOMZone.js (Traditionnel)Click/HTTP/TimerDéclenche vérification globaleMise à jour de tout l'arbreSignaux (Nouveau)Signal updateMise à jour uniquement des nœuds dépendants
User ActionZone.jsChange DetectionSignalsDOMUser ActionUser ActionZone.jsZone.jsChange DetectionChange DetectionSignalsSignalsDOMDOMZone.js (Traditionnel)Click/HTTP/TimerDéclenche vérification globaleMise à jour de tout l'arbreSignaux (Nouveau)Signal updateMise à jour uniquement des nœuds dépendants

Cycle de vie d’un signal Angular

Signal lifecycleCreatedReadUpdatedComputedEffectSignal writableSignal dérivé (computed)Side effect (effect)signal(initialValue)signal()set()/update()computed(() => ...)effect(() => ...)
Signal lifecycleCreatedReadUpdatedComputedEffectSignal writableSignal dérivé (computed)Side effect (effect)signal(initialValue)signal()set()/update()computed(() => ...)effect(() => ...)

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

Identifier les composantsCatégories@Input/@Output simplesState management complexeServices lourds RxJS@Input/@Output simples -> Utiliser input()/output() migrationState management complexe -> Convertir BehaviorSubject en signal()Services lourds RxJS -> Garde RxJS + toSignal/toObservableTest & ValidationAll components done?noyesRetour identifier composantsRetirer Zone.js - Activer mode zoneless
Identifier les composantsCatégories@Input/@Output simplesState management complexeServices lourds RxJS@Input/@Output simples -> Utiliser input()/output() migrationState management complexe -> Convertir BehaviorSubject en signal()Services lourds RxJS -> Garde RxJS + toSignal/toObservableTest & ValidationAll components done?noyesRetour identifier composantsRetirer Zone.js - Activer mode zoneless

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.

Terminé