🎬 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 ```puml @startuml object "Signal : name" as A object "Signal : surname" as B object "Computed : fullName" as C object "Effect : Log changes" as D object "Component Template" as E A --> C B --> C C --> D C --> E ' Pour la couleur : A -[#skyblue]-> C B -[#skyblue]-> C C -[#mediumpurple]-> D C -[#lightgreen]-> E ' Pour un fond coloré, vous pouvez utiliser : ' skinparam object { ' BackgroundColor<
> SkyBlue ' BackgroundColor<
> Thistle ' BackgroundColor<
> Wheat ' BackgroundColor<
> LightGreen ' } ' A <
> ' B <
> ' C <
> ' D <
> ' E <
> @enduml ``` #### Cycle de détection Zone.js vs Signaux ```puml @startuml participant "User Action" as U participant "Zone.js" as Z participant "Change Detection" as CD participant "Signals" as S participant "DOM" as D == Zone.js (Traditionnel) == U -> Z: Click/HTTP/Timer Z -> CD: Déclenche vérification globale CD -> D: Mise à jour de tout l'arbre == Signaux (Nouveau) == U -> S: Signal update S -> D: Mise à jour uniquement des nœuds dépendants @enduml ``` #### Cycle de vie d’un signal Angular ```puml @startuml state "Signal lifecycle" as SL { [*] --> Created : signal(initialValue) Created --> Read : signal() Read --> Updated : set()/update() Updated --> Read Read --> Computed : computed(() => ...) Computed --> Effect : effect(() => ...) Effect --> [*] note right of Created Signal writable end note note right of Computed Signal dérivé (computed) end note note right of Effect Side effect (effect) end note } @enduml ``` --- ## 3. Exemples pratiques ### Création et manipulation de signaux #### Créer un signal ```typescript import { signal } from '@angular/core'; const count = signal(0); // Signal avec valeur initiale ``` #### Lire la valeur ```typescript console.log(count()); // 0 ``` #### Mettre à jour ```typescript count.set(5); count.update(v => v + 1); ``` --- ### Signaux dérivés (computed) ```typescript 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 : ```typescript 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 ```typescript @Component({ selector: 'counter', template: `
Compteur
Valeur : {{ count() }}
+1
`, 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 : ```typescript @Component({ selector: 'user-card', template: `
{{ name }}
` }) export class UserCardComponent { @Input() name!: string; @Output() onClick = new EventEmitter
(); } ``` Après (avec signaux) : ```typescript import { input, output } from '@angular/core'; @Component({ selector: 'user-card', template: `
{{ name() }}
` }) export class UserCardComponent { name = input.required
(); onClick = output
(); } ``` 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 : ```typescript const count$ = new BehaviorSubject(0); count$.subscribe(value => console.log(value)); count$.next(count$.value + 1); ``` Après (signal) : ```typescript const count = signal(0); console.log(count()); // 0 count.set(count() + 1); // 1 ``` Pour bénéficier des opérateurs RxJS (debaseTime, filter…), utilisez ```typescript 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 ```puml @startuml start :Identifier les composants; partition "Catégories" { :@Input/@Output simples; :State management complexe; :Services lourds RxJS; } :@Input/@Output simples -> Utiliser input()/output() migration; :State management complexe -> Convertir BehaviorSubject en signal(); :Services lourds RxJS -> Garde RxJS + toSignal/toObservable; :Test & Validation; if (All components done?) then (no) :Retour identifier composants; else (yes) :Retirer Zone.js - Activer mode zoneless; endif stop @enduml ``` --- ## 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é