AngularAtlaséd. 22.2

Chap. 14Réactivité avancée61 / 75

Les Signals sous le capot

Le graphe réactif : producteurs et consommateurs, push-pull, pourquoi computed est lazy et sans glitch, le problème du diamant et quand les effects s’exécutent.

architecture ~0 min de lecture Documentation officielle

Sur la page signal() et computed(), vous avez vu ce que font les signals. Cette page montre comment ils fonctionnent de l’intérieur : un graphe réactif construit automatiquement à partir des lectures, dans lequel les écritures ne font que pousser une notification, et le recalcul n’a lieu que lorsque quelqu’un lit. Toutes les propriétés utiles en découlent : computed est lazy et mémoïsé, vous ne voyez jamais d’états intermédiaires incohérents, et Angular peut re-rendre uniquement les composants concernés, sans Zone.js.

Pourquoi c’est important
Une fois le modèle compris, vous pouvez deviner beaucoup de choses vous-même : pourquoi un computed coûteux ne coûte rien si personne ne le lit, pourquoi items().push() ne met pas à jour l’UI, pourquoi un effect qui modifie un autre signal est une mauvaise idée, ou pourquoi un signal lu dans une branche if « ne réagit » parfois pas.

Producers, consumers et le graphe

Chaque signal est un nœud du graphe. signal() est un producer. computed() est à la fois un consumer (il lit d’autres signals) et un producer (d’autres le lisent). effect() et les templates de composants sont des consumers. Pendant l’exécution d’une fonction réactive, il existe un consumer actif, et chaque signal lu à ce moment devient sa dépendance. Le graphe n’est pas déclaré : il est construit à partir des lectures.

Chaque nœud a une version qui n’augmente que lorsqu’il produit une valeur différente de la précédente. Certains consumers sont « live » (effects et templates) ; pour eux, le graphe conserve aussi des arêtes inverses, du producer vers le consumer, afin de pouvoir leur envoyer des notifications. Les types primitifs de @angular/core/primitives/signals montrent exactement ces champs : version, dirty, producers, consumers.

const a = signal(1);                                  // producer
const b = signal(2);                                  // producer
const sum = computed(() => a() + b());                // consumer ET producer
const product = computed(() => a() * b());
const result = computed(() => sum() + product());     // losange : a et b arrivent ici par deux chemins

// Pendant l’exécution d’une fonction réactive, Angular garde un « consumer actif ».
// Chaque signal lu à ce moment devient sa dépendance. Vous ne déclarez rien : le graphe est construit à partir des lectures.

Push, puis pull

Une écriture (set/update) ne recalcule rien. Elle augmente seulement la version du signal et prévient les consumers live qu’ils pourraient être dirty : c’est la phase push. Quand le consumer s’exécute à nouveau (le template au prochain rendu, l’effect à son tour), il lit ce dont il a besoin, et chaque computed vérifie ses producers : une version a-t-elle changé ? Alors seulement il est recalculé. C’est la phase pull.

a.set(5)
  │  PUSH : a incrémente sa `version` et prévient les consumers « live » (template, effect) qu’ils PEUVENT être dirty.
  │        Rien n’est encore recalculé.
  ▼
template / effect marqué dirty  →  la Change Detection planifie un rendu
  │
  ▼
le template lit result()
  │  PULL : result vérifie ses producers (sum, product) — ont-ils une version différente ?
  │        sum vérifie ses producers (a, b) — a a une version différente → sum est recalculé.
  │        Si la nouvelle valeur est ÉGALE à l’ancienne, la version de sum n’augmente pas → la propagation s’arrête.
  ▼
result est recalculé une fois, avec des valeurs déjà cohérentes (glitch-free)

La conséquence importante : un computed n’est recalculé qu’après la fin de toutes les écritures, avec des valeurs déjà cohérentes. Dans le losange de la démo, result ne voit jamais un nouveau sum avec un ancien product. C’est ce qu’on appelle glitch-free. Et si vous modifiez a et b dans le même clic, result n’est recalculé qu’une seule fois.

LiveLe graphe réactif, instrumenté (vrais signals)
a = 1signalb = 2signal sum = calculé × product = calculé × result = calculé × template consumer

Dans le template : a = 1, b = 2 · result = 5

Ce que result a « vu » à chaque recalcul :
    Appuyez sur « a + 1 » : sum, product et result sont chacun recalculés une fois. Appuyez sur le bouton qui modifie les deux : toujours un seul recalcul. Décochez « le template lit result » et modifiez a : plus rien n’est calculé (lazy). Le journal montre que result voit toujours des paires cohérentes.

    Lazy et mémoïsation

    La documentation d’Angular le dit directement : un computed est évalué paresseusement (lazy) et mémoïsé. La fonction ne s’exécute pas avant la première lecture. Ensuite, la valeur est conservée et réutilisée jusqu’à ce qu’une dépendance change. En pratique, vous pouvez placer des dérivations coûteuses (filtrage, tri) dans un computed : elles ne coûtent quelque chose que lorsque le résultat est réellement nécessaire.

    const expensive = computed(() => {
      console.log('calcul…');
      return items().filter(matches).sort(byPrice);
    });
    
    items.set(newItems);  // rien ne s’affiche : personne n’a encore lu
    expensive();          // « calcul… » — première lecture
    expensive();          // rien : valeur mémoïsée
    items.set(other);     // toujours rien — marqué comme périmé, non recalculé
    expensive();          // « calcul… »

    Égalité : là où la propagation s’arrête

    La version d’un nœud n’augmente que si la nouvelle valeur n’est pas « égale » à l’ancienne. Par défaut, l’égalité est Object.is : pour les objets et les tableaux, c’est la référence qui compte, pas le contenu. Le même mécanisme s’applique à un computed. Si son résultat ne change pas, ses consumers ne sont pas recalculés, même si ses dépendances ont changé. Avec { equal }, vous définissez ce que « identique » veut dire.

    // Par défaut : Object.is. Pour les objets/tableaux, c’est la RÉFÉRENCE qui compte.
    const tags = signal(['a', 'b']);
    tags.set(['a', 'b']);           // autre référence → « nouvelle » valeur → les consumers se recalculent
    
    // Égalité personnalisée : la valeur ne change pas si elle est « identique » selon votre règle
    const sameItems = (x: string[], y: string[]) => x.length === y.length && x.every((v, i) => v === y[i]);
    const stableTags = signal(['a', 'b'], { equal: sameItems });
    stableTags.set(['a', 'b']);     // égal → la version n’augmente pas → rien en aval
    
    // Un computed a aussi une égalité : si son résultat est égal, la propagation s’arrête là
    const parity = computed(() => (n() % 2 ? 'impair' : 'pair'));   // n: 0 → 2 → 4 … parity reste 'pair'
    LiveCoupure par égalité, avec des compteurs
    n = 0 · Le nombre est pair
    NœudValeurRecalculs
    parity = computed(() => n() % 2 ? 'impair' : 'pair')
    label = computed(() => 'Le nombre est ' + parity())
    Signalcount = computed(tags().length)
    signal(...) — égalité par défaut (Object.is) 2 · recalculé ×
    signal(..., { equal: sameItems }) 2 · recalculé ×
    Appuyez plusieurs fois sur « n + 2 » : parity est recalculé (n a changé), mais reste « pair », donc label n’est PLUS recalculé. Ensuite, réaffectez le même tableau : avec l’égalité par défaut, count est recalculé ; avec le equal personnalisé, il ne l’est pas.

    Dépendances dynamiques

    À chaque exécution, la liste des producers d’un consumer est reconstruite à partir de ce qu’il a réellement lu. Un signal lu uniquement dans une branche if n’est une dépendance que tant que cette branche s’exécute. C’est un gain de performance, mais aussi une source de confusion : « je modifie details et rien ne se passe » est généralement normal, car la branche courante ne le lit pas.

    const view = computed(() => {
      if (showDetails()) {
        return details();     // dépendances : showDetails + details
      }
      return summary();       // dépendances : showDetails + summary
    });
    // À chaque exécution, la liste des producers est reconstruite à partir de ce qui a été EFFECTIVEMENT lu.
    // Tant que showDetails() est false, modifier details ne recalcule pas view.
    LiveCe qu’un computed lit, à chaque exécution

    view() = summary v0

    Dépendances enregistrées lors de la dernière exécution : · view recalculé ×

    Avec showDetails = false, appuyez sur « modifier details » : view n’est pas recalculé, car il ne dépend pas de details. Basculez showDetails et recommencez : maintenant details est une dépendance, et summary n’en est plus une.

    Effects et templates : les consumers live

    Les effects s’exécutent de façon asynchrone, pendant le processus de Change Detection, et non au moment du set(). La documentation v22 distingue deux types. Un view effect est créé dans le contexte d’un composant : il s’exécute avant la vérification de ce composant et est détruit avec lui. Un root effect est créé dans un service fourni dans root : il s’exécute avant la vérification de tous les composants et vit aussi longtemps que l’application. Si une dépendance change pendant l’exécution de l’effect, celui-ci s’exécute une fois de plus avant que la Change Detection ne continue.

    // View effect : créé dans le contexte d’un composant → s’exécute AVANT que ce composant
    // soit vérifié par la Change Detection ; il est détruit avec le composant.
    @Component({ /* ... */ })
    export class Profile {
      constructor() {
        effect(() => console.log('profil', this.user()));
      }
    }
    
    // Root effect : créé dans un service fourni dans root → s’exécute avant la vérification de tous les composants ;
    // il vit aussi longtemps que l’application.
    @Injectable({ providedIn: 'root' })
    export class Analytics {
      constructor() {
        effect(() => track(this.route()));
      }
    }
    // Les deux sont asynchrones (ils ne s’exécutent pas au moment du set()) et liés au processus de Change Detection.

    Le template d’un composant est aussi un consumer réactif. Quand un signal lu dans le template change, Angular marque le composant pour vérification (comme markForCheck), et la Change Detection ne re-rend que lui et ses ancêtres. C’est sur cela que reposent les applications zoneless et l’OnPush par défaut de la v22 (voir Change detection).

    @Component({
      template: `<p>{{ fullName() }}</p>`,   // le template est un consumer réactif
    })
    export class Card {
      readonly first = signal('Ana');
      readonly fullName = computed(() => `${this.first()} Pop`);
    }
    // first.set('Ioana') → le nœud réactif du template est marqué dirty → le composant est marqué pour
    // vérification (comme markForCheck) → la Change Detection le re-rend. Pas de Zone.js, pas de vérification
    // de tout l’arbre : c’est ainsi que fonctionnent les applications zoneless.

    Quand OUI, quand NON

    Utilisez le modèle à votre avantage
    • Des dérivations coûteuses dans un computed : elles ne sont calculées qu’en cas de besoin et une seule fois par changement.
    • Des chaînes de computed plutôt qu’un seul computed géant. Avec la coupure par égalité, les petits changements s’arrêtent tôt.
    • { equal } pour les valeurs souvent recréées mais « identiques » (filtres, listes d’identifiants venant du serveur).
    • untracked pour les lectures qui ne doivent rien déclencher (logging, analytics).
    Ce que le graphe ne fait pas pour vous
    • Il ne détecte pas les mutations : un push sur un tableau dans un signal ne notifie personne.
    • Il n’ordonne pas vos effets de bord : si vous avez besoin d’un ordre strict dans le temps, utilisez RxJS (voir RxJS avancé).
    • Ce n’est pas un mécanisme d’événements : « un clic a eu lieu » n’est pas un état. Utilisez output() ou un Subject.
    • Il n’exécute pas les effects de façon synchrone : si vous avez besoin du résultat immédiatement, dérivez-le avec computed.

    Pièges fréquents

    // ✗ Écriture dans un computed — erreur : "Writing to signals is not allowed in a `computed`"
    const total = computed(() => {
      count.set(items().length);       // effet de bord dans une dérivation
      return items().reduce(sum, 0);
    });
    
    // ✗ Effect utilisé pour une dérivation (un rendu de plus, risque de boucles)
    effect(() => total.set(items().reduce(sum, 0)));
    
    // ✓ La dérivation est un computed
    const total2 = computed(() => items().reduce(sum, 0));
    
    // ✗ Mutation : même référence → Object.is dit « égal » → rien ne se met à jour
    items().push(newItem);
    // ✓
    items.update((list) => [...list, newItem]);
    • « Ça ne réagit pas » : vérifiez si le signal est lu dans la branche qui s’exécute, et si vous le lisez bien avec (). Une référence à un signal sans l’appeler ne crée pas de dépendance.
    • De gros objets dans un seul signal : tout champ modifié recalcule tout ce qui dépend de l’objet. Découpez l’état, ou dérivez les champs avec computed.
    • Un effect qui écrit dans d’autres signals : rendus supplémentaires et risque de boucles. Presque toujours, la réponse est un computed ou un linkedSignal.
    • Lire dans le constructeur « pour que ce soit prêt » : un computed lu trop tôt est calculé avec des données incomplètes. Laissez-le lazy.

    Aide-mémoire

    NŒUD          RÔLE                    QUAND IL S’EXÉCUTE
    signal        producer                — (il ne change que sa valeur et sa version lors d’un set/update)
    computed      consumer + producer     lazy : à la lecture, seulement si une dépendance a changé
    effect        consumer « live »       de façon asynchrone, pendant la Change Detection (view : avant le composant ; root : avant tous)
    template      consumer « live »       quand le composant est re-rendu après avoir été marqué dirty
    
    RÈGLES
      dépendances = ce que vous avez EFFECTIVEMENT lu lors de la dernière exécution (dynamiques, par branche)
      égalité par défaut Object.is ; { equal } pour les valeurs « identiques » → stoppe la propagation
      un computed sans lecteurs n’est pas calculé du tout
      n’écrivez pas de signals dans un computed ; ne dérivez pas d’état avec un effect
      untracked(() => …) lit sans créer de dépendance

    Chap. 14 · 0 sur 4 terminées

    Leçon terminée ?

    Marquez-la : elle apparaîtra sur votre carte, dans le sommaire et sur la page d’accueil.

    Se marque toute seule une fois la leçon lue en entier.

    1. Les Signals sous le capot
    2. RxJS avancé : opérateurs d’ordre supérieur
    3. NgRx SignalStore
    4. Machines à états avec les signals
    Voir toute votre carte

    Les 75 pages · tapez pour filtrer

    • 01Nouveautés d’Angular 21 & 22Tous les changements importants de la v21 et de la v22, en bref, avec un lien vers la page qui explique chacun.Départ
    • 01Votre première application, pas à pasUne liste de lecture construite d’un composant vide jusqu’à une application terminée — signals, @for, inputs et outputs, un service et un formulaire, pas à pas.Départ
    • 01Décisions rapides« Où mettre ça ? », « Signal ou Observable ? », « Shared ou feature ? » — des réponses courtes aux questions de tous les jours.Départ
    • 01Questions d’entretien Angular60 vraies questions d’entretien pour profils junior, confirmé et senior, avec la réponse à donner, la relance et les signaux d’alerte. Plus un entretien blanc.Départ
    • 02Standalone et bootstrapComment une application démarre sans NgModule : bootstrapApplication, app.config.ts, imports dans le composant.Fondamentaux modernes
    • 02Data binding et événementsInterpolation, bindings de propriétés et d’attributs, événements avec $event et filtres de touches, et comment [( )] est construit — ce qui entre dans le DOM et ce qui en revient.Fondamentaux modernes
    • 02Control Flow : @if, @for, @switchLa syntaxe de blocs qui remplace *ngIf/*ngFor/ngSwitch, avec track, @empty et vérifications d’exhaustivité.Fondamentaux modernes
    • 02@let et les nouvelles expressions de templateVariables locales avec @let, fonctions fléchées, spread, regex, instanceof, commentaires dans les balises.Fondamentaux modernes
    • 02@defer — Lazy Loading dans les templatesChargez les composants lourds uniquement quand c’est nécessaire : triggers, prefetch, placeholder, loading, error.Fondamentaux modernes
    • 02Bindings de classe et de stylePourquoi [class.x] et [style.x] ont remplacé ngClass et ngStyle (avec migration automatique depuis la v21).Fondamentaux modernes
    • 02Styles des composants et encapsulation:host, les trois modes d’encapsulation, les thèmes avec des variables CSS, et pourquoi ::ng-deep est un dernier recours.Fondamentaux modernes
    • 02Pipes : quand et commentPipes pures vs impures, pipes personnalisées, et comment choisir entre une pipe, computed() et une méthode.Fondamentaux modernes
    • 03signal() et computed()Les primitives réactives : état modifiable, état dérivé, égalité, untracked et règles d’immutabilité.Signals et réactivité
    • 03effect() — quand OUI, quand NONEffets de bord synchronisés avec les Signals : localStorage, logs, API impératives. Et leurs pièges.Signals et réactivité
    • 03linkedSignal() — état dérivé modifiableUn signal qui se réinitialise quand sa source change, mais que vous pouvez aussi modifier à la main.Signals et réactivité
    • 03resource, rxResource, httpResourceDes données asynchrones sous forme de Signals : status, value, error, reload, annulation automatique. Stable depuis la v22.Signals et réactivité
    • 03debounced() — signals différésLa version « debounce » d’un signal, sans RxJS. Parfait pour la recherche.Signals et réactivité
    • 03Listes : rechercher, filtrer, trier, paginerUne chaîne de signals computed qui ne recalcule que ce qui a changé, avec les filtres dans l’URL pour qu’un lien rouvre la même vue.Signals et réactivité
    • 03Les Observables depuis zéroCe qu’est un Observable, subscribe et unsubscribe, les opérateurs que vous utilisez chaque semaine, froid ou chaud — et quand un signal suffit.Signals et réactivité
    • 03Signals ↔ RxJStoSignal, toObservable, takeUntilDestroyed, outputFromObservable — et quand RxJS reste le bon choix.Signals et réactivité
    • 04input() et output()Les inputs comme Signals (required, transform, alias) et les outputs typés. Les données descendent, les événements remontent.Composants : communication
    • 04model() — binding bidirectionnelLe « banana-in-a-box » [( )] avec une seule API. Quand l’utiliser, et quand input + output est préférable.Composants : communication
    • 04viewChild et contentChildLes queries comme Signals : viewChild, viewChildren, contentChild, contentChildren, l’option read.Composants : communication
    • 04Lifecycle et DestroyRefQuels hooks comptent encore à l’ère des Signals, afterNextRender, DestroyRef et ngOnChanges typé.Composants : communication
    • 04Projection de contenung-content avec select, plusieurs slots, contenu par défaut et le piège de la projection conditionnelle.Composants : communication
    • 04ng-template et ngTemplateOutletLes templates comme valeurs : les render props en Angular, contexte typé et personnalisation des composants.Composants : communication
    • 05Smart vs DumbContainer vs présentation : qui détient l’état, qui se contente d’afficher, et comment bien les séparer.Conception des composants
    • 05Composants génériques <T>Un DataTable<T> ou un Select<T> correctement typé, avec des templates pour les cellules.Conception des composants
    • 05Composants dynamiquesNgComponentOutlet, createComponent avec inputBinding/outputBinding, et un tableau de bord construit à partir d’une configuration.Conception des composants
    • 05Directives et composition d’hôteLes directives d’attribut, les métadonnées host: {} et la Directive Composition API (hostDirectives).Conception des composants
    • 05Contrôles de formulaire personnalisésFormValueControl (Signal Forms) vs ControlValueAccessor : une notation par étoiles, dans les deux variantes.Conception des composants
    • 05Change DetectionOnPush est la valeur par défaut depuis la v22, Zone.js a disparu depuis la v21. Ce qui déclenche le rendu et comment éviter les mauvaises surprises.Conception des composants
    • 06inject() et @Service()Le nouveau décorateur @Service() de la v22, inject() vs le constructeur, et le contexte d’injection.Dependency Injection
    • 06Providers et hiérarchie des injecteursRoot vs route vs composant, useClass/useValue/useFactory/useExisting, et les modificateurs optional/self/skipSelf.Dependency Injection
    • 06InjectionToken et provideX()Configuration typée, fonctions provideFeature(), makeEnvironmentProviders et initializers.Dependency Injection
    • 06injectAsync() — services en Lazy LoadingChargez un service lourd uniquement quand vous en avez besoin, avec prefetch à l’idle.Dependency Injection
    • 07Structure des dossierscore / shared / features, ce que contient chacun, et à quoi ressemble l’intérieur d’une feature.Architecture d’application
    • 07Shared vs featureQuand déplacer un composant dans shared et (surtout) quand NE PAS le faire. Une checklist concrète.Architecture d’application
    • 07Interfaces et modèlesInterface en ligne ou fichier séparé ? DTO vs modèle de domaine, type vs interface, où les ranger.Architecture d’application
    • 07State Management avec les SignalsStores simples à base de Signals, facades, état local vs global, et quand NgRx devient nécessaire.Architecture d’application
    • 07Guide de style et nommageLes conventions officielles actuelles : noms de fichiers sans suffixes, protected, readonly, ordre des membres.Architecture d’application
    • 07Barrels, alias et frontièresindex.ts, alias de chemins, API publique d’une feature et règles de dépendances vérifiables automatiquement.Architecture d’application
    • 07Erreurs : @boundary et ErrorHandlerLe nouveau bloc @boundary de la v22.2, l’ErrorHandler global, les erreurs HTTP et les erreurs de resource.Architecture d’application
    • 08Routing moderne et Lazy LoadingloadComponent, routes enfants, paramètres comme inputs et héritage des paramètres (le nouveau comportement par défaut de la v22).Routing
    • 08Guards et resolverscanActivate, canMatch, canDeactivate et resolve sous forme de fonctions, avec redirections via UrlTree / RedirectCommand.Routing
    • 09Signal Forms — les basesDes formulaires bâtis sur les Signals : form(), [formField], validation par schéma. Stable depuis la v22.Formulaires
    • 09Signal Forms — avancéValidation personnalisée et inter-champs, async/HTTP avec debounce, listes dynamiques, désactivé/masqué, schémas réutilisables.Formulaires
    • 09Reactive Forms typésFormGroup typé, nonNullable, FormArray — et leur cohabitation avec les Signal Forms.Formulaires
    • 10HttpClient, interceptors et couche d’APIHttpClient sans configuration (v21), Fetch par défaut (v22), interceptors fonctionnels et une couche d’API avec mapping des DTO.HTTP et données
    • 11Performance — une checklistCe qui compte vraiment : OnPush + Signals, track, @defer, routes en Lazy Loading, images, bundles et Hydration.Qualité et performance
    • 11Tests avec VitestVitest est le runner par défaut depuis la v21. TestBed, composants avec Signals, services, HTTP et harnesses.Qualité et performance
    • 11DevTools : regarder dans votre applicationChrome DevTools et Angular DevTools pas à pas : ce que montre chaque panneau, avec des exercices qui fonctionnent sur ce site.Qualité et performance
    • 11Angular AriaDes composants headless et accessibles que vous stylez comme vous voulez : tabs, accordion, menu, listbox...Qualité et performance
    • 12SSR, Prerendering et modes de renduQuand rendre sur le serveur, quand au build et quand uniquement dans le navigateur : RenderMode par route, app.routes.server.ts et les coûts réels.SSR et Hydration
    • 12Hydration : event replay et incrémentaleComment le navigateur reprend le HTML du serveur sans le re-rendre : Hydration complète, event replay, @defer (hydrate on …) et erreurs de mismatch.SSR et Hydration
    • 12Code compatible serveurwindow n’existe pas sur le serveur : PLATFORM_ID, afterNextRender, DOCUMENT, TransferState, le cache HTTP et PendingTasks.SSR et Hydration
    • 13Micro frontendsQuand découper une application en plusieurs déploiements vaut le coup, et quand c’est juste de la complexité : Native Federation, Module Federation, iframes, Web Components.Architecture à grande échelle
    • 13Web Components avec Angular ElementscreateCustomElement : un composant Angular utilisé comme balise HTML n’importe où, plus la communication entre applications via des événements et BroadcastChannel.Architecture à grande échelle
    • 13Monorepo, bibliothèques et frontièresUn workspace avec plusieurs applications et bibliothèques : ng generate library, Nx, points d’entrée secondaires, publication et règles de dépendances.Architecture à grande échelle
    • 13Architecture hexagonale (ports et adapters)Un domaine sans Angular, des ports sous forme de classes abstraites et des adapters interchangeables : HTTP, localStorage ou en mémoire, sans toucher à l’UI.Architecture à grande échelle
    • 14Les Signals sous le capotLe graphe réactif : producteurs et consommateurs, push-pull, pourquoi computed est lazy et sans glitch, le problème du diamant et quand les effects s’exécutent.Réactivité avancée
    • 14RxJS avancé : opérateurs d’ordre supérieurswitchMap, mergeMap, concatMap et exhaustMap sur un diagramme interactif, plus retry avec backoff, shareReplay et comment éviter les fuites.Réactivité avancée
    • 14NgRx SignalStoreState Management à grande échelle sur les Signals : withState, withComputed, withMethods, rxMethod, entités et hooks — et quand un store simple suffit.Réactivité avancée
    • 14Machines à états avec les signalsFini les états impossibles : une machine typée sur des signals, avec transitions, gardes et un diagramme d’états en direct.Réactivité avancée
    • 15WebSockets : un temps réel qui résiste au réseauUn socket typé sous forme de signals : reconnexion avec backoff et jitter, heartbeat, file d’attente hors ligne et sécurité SSR.Temps réel et hors ligne
    • 15Mises à jour optimistes et rollbackUne UI instantanée qui reste honnête : snapshot et rollback, réponses dans le désordre, clés d’idempotence.Temps réel et hors ligne
    • 15Offline-first : Service Worker et cacheLe Service Worker d’Angular : stratégies de cache, annonce des nouvelles versions et file d’attente pour les modifications faites hors ligne.Temps réel et hors ligne
    • 16Web WorkersDes calculs lourds sans bloquer l’UI : un vrai worker dans la page, l’échange de messages et quand ça n’en vaut pas la peine.UI avancée et performance
    • 16Listes géantes : virtual scroll100 000 lignes sans latence : le virtual scroll du CDK, itemSize, trackBy et les alternatives (pagination, scroll infini).UI avancée et performance
    • 16Animations modernes et View Transitionsanimate.enter et animate.leave à la place de @angular/animations (déprécié), transitions CSS et View Transitions à la navigation.UI avancée et performance
    • 16Fuites mémoire : les trouver et les corrigerAbonnements, timers et listeners qui survivent à leur composant — comment les repérer dans les DevTools et les fermer.UI avancée et performance
    • 17Sécurité : XSS, sanitisation et CSPCe qu’Angular fait automatiquement contre le XSS, quand bypassSecurityTrust devient dangereux, plus la CSP avec nonce, Trusted Types et XSRF.Sécurité et internationalisation
    • 17Authentification : tokens et refreshUn interceptor qui ajoute le token, le renouvelle une seule fois sur une 401 et rejoue les requêtes en attente — plus où conserver le token.Sécurité et internationalisation
    • 17OAuth 2.1 et OpenID Connect avec PKCELe flux authorization code avec PKCE, pas à pas : verifier et challenge, state et nonce, l’ID token, et pourquoi un BFF est plus sûr.Sécurité et internationalisation
    • 17Internationalisation (i18n)$localize, l’attribut i18n, ICU plural/select, un build par langue vs des traductions à l’exécution, et le formatage selon la locale.Sécurité et internationalisation