AngularAtlaséd. 22.2

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

NgRx SignalStore

State Management à grande échelle sur les Signals : withState, withComputed, withMethods, rxMethod, entités et hooks — et quand un store simple suffit.

architecture ~0 min de lecture

NgRx SignalStore (le paquet @ngrx/signals) est une façon déclarative de construire des Stores au-dessus des Signals. Vous composez des features (withState, withComputed, withMethods, withHooks, withEntities…), et le résultat est une classe injectable. Son état est exposé sous forme de Signals et ne peut être modifié que par les méthodes que vous définissez.

C’est l’étape suivante après le Store artisanal de la page State Management : les mêmes idées, mais avec une structure standard, des entités prêtes à l’emploi, une intégration RxJS pour les flux asynchrones et des morceaux réutilisables d’un Store à l’autre. La version utilisée ici est @ngrx/signals 22, compatible avec Angular 22.

Pourquoi c’est important
À mesure que l’application grandit, les Stores écrits à la main finissent par tous être différents : certains exposent des Signals modifiables, d’autres non ; certains gardent loading/error, d’autres non. SignalStore vous donne une seule forme pour tous. Et les parties répétitives (état d’une requête, pagination, collections d’entités), vous ne les écrivez qu’une fois, sous forme de features.

Anatomie d’un Store

Un signalStore(...) est une liste de features appliquées dans l’ordre. Chaque feature voit ce qu’ont ajouté les précédentes. withState définit l’état : chaque propriété devient un Signal (store.todos()), et les objets imbriqués deviennent des deep signals. withComputed ajoute l’état dérivé, withMethods ajoute les opérations. Seules les méthodes peuvent appeler patchState. L’état est protégé par défaut : impossible de le modifier directement depuis l’extérieur du Store.

import { computed } from '@angular/core';
import { patchState, signalStore, withComputed, withMethods, withState } from '@ngrx/signals';

type Filter = 'all' | 'open' | 'done';
interface Todo { id: number; title: string; done: boolean }

export const TodoStore = signalStore(
  { providedIn: 'root' }, // sans l’option : mettez-le dans `providers` là où vous en avez besoin
  withState({ todos: [] as Todo[], filter: 'all' as Filter }),
  withComputed(({ todos, filter }) => ({
    visible: computed(() =>
      filter() === 'all' ? todos() : todos().filter((t) => t.done === (filter() === 'done')),
    ),
    openCount: computed(() => todos().filter((t) => !t.done).length),
  })),
  withMethods((store) => ({
    add(title: string): void {
      patchState(store, (s) => ({ todos: [...s.todos, { id: Date.now(), title, done: false }] }));
    },
    toggle(id: number): void {
      patchState(store, (s) => ({ todos: s.todos.map((t) => (t.id === id ? { ...t, done: !t.done } : t)) }));
    },
    setFilter(filter: Filter): void {
      patchState(store, { filter });
    },
  })),
);

patchState reçoit soit un objet partiel ({ filter }), soit une fonction (state) => Partial<State>. Il accepte aussi autant d’updaters que vous voulez, appliqués ensemble. L’état est immuable : vous renvoyez toujours de nouveaux objets et tableaux, sinon les Signals ne remarquent pas le changement (même règle que pour signal()).

Où vit le Store

Comme pour tout Service, la durée de vie d’un Store dépend de l’injector où vous le fournissez. Avec { providedIn: 'root' }, vous obtenez une seule instance globale, adaptée à la session utilisateur ou à un panier. Sans cette option, vous le mettez dans les providers d’un composant ou d’une route et vous obtenez une nouvelle instance, détruite avec lui. C’est le bon choix pour l’état d’une page.

// 1. Global : une seule instance pour toute l’application
export const SessionStore = signalStore({ providedIn: 'root' }, withState({ user: null as User | null }));

// 2. Par page / par composant : une nouvelle instance, détruite avec le composant
export const ProductsStore = signalStore(withEntities<Product>() /* ... */);

@Component({
  providers: [ProductsStore], // chaque <app-catalog> a son propre Store
  template: `...`,
})
export class Catalog {
  readonly store = inject(ProductsStore);
}

// 3. Par route (feature) : vit aussi longtemps que la route
{ path: 'catalog', providers: [ProductsStore], loadComponent: () => import('./catalog') }
LiveStore local ou Store root
Instance 3
local: 0
root: 0
Instance 4
local: 0
root: 0
« local » meurt avec le composant ; « root » survit.
Appuyez sur « local +1 » dans chaque carte : chacune a sa propre instance. « root +1 » incrémente le même compteur dans les deux. Détruisez les cartes puis recréez-les : le compteur local repart de zéro, le compteur root garde sa valeur.

Entités

withEntities<Product>() ajoute une collection normalisée : dans l’état, vous avez ids et entityMap, et côté Signal, entities(), un tableau dérivé. Les updaters de @ngrx/signals/entities (setAllEntities, addEntity, updateEntity, upsertEntity, removeEntity…) se passent directement à patchState. Pour plusieurs collections dans le même Store, il existe des collections nommées (withEntities({ entity: type<Product>(), collection: 'product' }) → productEntities()).

Flux asynchrones avec rxMethod

rxMethod (de @ngrx/signals/rxjs-interop) transforme un pipe RxJS en méthode. Vous pouvez l’appeler avec une valeur, un Observable ou un Signal. Avec un Signal, la méthode s’exécute à chaque changement de celui-ci. L’appel store.search(store.query) dans onInit lie la recherche à la requête une seule fois. Le reste, c’est du RxJS ordinaire : debounceTime, switchMap pour annuler la requête périmée, catchError à l’intérieur du switchMap, pour qu’une erreur n’arrête pas le flux (voir RxJS avancé). L’abonnement se ferme tout seul quand le Store est détruit.

import { HttpClient } from '@angular/common/http';
import { inject } from '@angular/core';
import { patchState, signalStore, withHooks, withMethods, withState } from '@ngrx/signals';
import { setAllEntities, updateEntity, withEntities } from '@ngrx/signals/entities';
import { rxMethod } from '@ngrx/signals/rxjs-interop';
import { catchError, debounceTime, distinctUntilChanged, EMPTY, pipe, switchMap, tap } from 'rxjs';

export const ProductsStore = signalStore(
  withEntities<Product>(),                 // état : ids + entityMap ; Signal : entities()
  withState({ query: '' }),
  withRequestStatus(),                     // feature personnalisée (ci-dessous)
  withMethods((store, http = inject(HttpClient)) => ({
    setQuery: (query: string) => patchState(store, { query }),
    buy: (id: number) =>
      patchState(store, updateEntity({ id, changes: (p) => ({ stock: p.stock - 1 }) })),

    search: rxMethod<string>(
      pipe(
        debounceTime(300),
        distinctUntilChanged(),
        tap(() => patchState(store, setPending())),
        switchMap((q) =>
          http.get<Page<Product>>(`/api/products?q=${encodeURIComponent(q)}`).pipe(
            tap((page) => patchState(store, setAllEntities(page.items), setFulfilled())),
            catchError(() => {
              patchState(store, setError('Le serveur ne répond pas.'));
              return EMPTY; // le flux externe reste vivant
            }),
          ),
        ),
      ),
    ),
  })),
  withHooks({
    onInit(store) {
      store.search(store.query); // rxMethod reçoit un SIGNAL : elle s’exécute à chaque changement
    },
  }),
);
tapResponse
Dans la documentation de NgRx, vous verrez souvent tapResponse à la place de tap + catchError. Il fait la même chose de façon plus compacte, mais vient du paquet séparé @ngrx/operators. L’exemple ici n’utilise que RxJS.

Features réutilisables

signalStoreFeature(...) regroupe plusieurs features dans une fonction que vous ajoutez à n’importe quel Store, par exemple withRequestStatus() : l’état de la requête plus isPending et error. Les updaters sont de simples fonctions qui renvoient un morceau d’état, vous pouvez donc les combiner avec d’autres dans le même patchState. Cela supprime le code loading/error copié-collé dans chaque Store.

import { computed } from '@angular/core';
import { signalStoreFeature, withComputed, withState } from '@ngrx/signals';

export type RequestStatus = 'idle' | 'pending' | 'fulfilled' | { error: string };

// Un morceau de Store réutilisable : état + computeds, composé dans n’importe quel signalStore
export function withRequestStatus() {
  return signalStoreFeature(
    withState<{ requestStatus: RequestStatus }>({ requestStatus: 'idle' }),
    withComputed(({ requestStatus }) => ({
      isPending: computed(() => requestStatus() === 'pending'),
      error: computed(() => {
        const s = requestStatus();
        return typeof s === 'object' ? s.error : null;
      }),
    })),
  );
}

// Updaters : de simples fonctions qui renvoient un morceau d’état → patchState(store, setPending())
export const setPending = () => ({ requestStatus: 'pending' as RequestStatus });
export const setFulfilled = () => ({ requestStatus: 'fulfilled' as RequestStatus });
export const setError = (error: string) => ({ requestStatus: { error } as RequestStatus });
LiveStore de produits : entités + rxMethod + feature personnalisée
  • Aucun produit.
getState(store) — live
{
  "query": "",
  "inStockOnly": false,
  "cart": 0,
  "requestStatus": "idle",
  "ids": []
}
Dans le panier : 0 · valeur du stock : 0 RON
Tapez dans la recherche : la requête ne part qu’après une pause de 300 ms. « Acheter » modifie une seule entité avec updateEntity. À droite, vous voyez getState(store) mis à jour en direct.

Le reste de la boîte à outils

signalState est l’option « sans Store » : un objet de Signals pour l’état local d’un composant, toujours avec patchState. withProps ajoute des propriétés qui ne sont pas de l’état (services, resources, observables). withLinkedState fait pour un Store ce que linkedSignal fait pour un composant. getState donne un instantané de l’état, et watchState vous notifie de façon synchrone à chaque changement. Pour les tests, unprotected(store) de @ngrx/signals/testing permet de mettre le Store dans un état donné. Le paquet propose aussi un plugin d’événements (@ngrx/signals/events), pour l’approche façon Redux avec événements et reducers.

import { patchState, signalState } from '@ngrx/signals';

// Pour l’état local d’UN composant : pas de Store, juste un objet de Signals
@Component({ /* ... */ })
export class Filters {
  readonly state = signalState({ query: '', page: 1, sort: 'name' as 'name' | 'price' });

  next(): void {
    patchState(this.state, (s) => ({ page: s.page + 1 }));
  }
  // dans le template : state.query(), state.page()
}

Quand OUI, quand NON

Utilisez SignalStore
  • Un état de feature utilisé par plusieurs composants, avec des opérations asynchrones (recherche, enregistrement, pagination).
  • Des collections d’entités mises à jour souvent (listes éditables, paniers, tableaux).
  • Plusieurs équipes qui veulent la même forme pour tous les Stores, et des features réutilisables.
  • Vous avez du NgRx Store classique et voulez quelque chose de plus léger pour les nouvelles features. Les deux peuvent coexister.
Quelque chose de plus simple suffit
  • L’état d’un seul composant : signal() ou signalState.
  • Des données lues depuis le serveur et simplement affichées : httpResource (voir resource).
  • Un petit Service avec deux Signals et trois méthodes : un Store artisanal est plus clair pour tout le monde.
  • Des applications où personne dans l’équipe ne connaît NgRx et où l’échéance est demain. La dépendance a un coût d’apprentissage.
Store artisanalSignalStoreNgRx Store (Redux)
Boilerplateminimalfaibleimportant (actions, reducers, effects, selectors)
Structure standardselon ce que décide l’équipeouioui, très stricte
Entitésà écrire soi-mêmewithEntities@ngrx/entity
Asyncresource / RxJSrxMethod, resourceeffects
Idéal pourpetites featuresla plupart des applicationsflux complexes, audit d’événements

Pièges fréquents

  • Muter l’état (s.todos.push(x) dans un updater). Le Signal reçoit la même référence et rien ne se met à jour. Renvoyez toujours de nouveaux tableaux et objets.
  • providedIn: 'root' pour l’état d’une page. L’état « colle » d’une visite à l’autre et se mélange entre les instances du composant. Mettez le Store dans les providers de la page.
  • catchError placé après switchMap dans une rxMethod. La première erreur ferme le flux et la recherche ne fonctionne plus. Placez-le dans le flux interne.
  • De la logique métier dans les composants, avec le Store utilisé comme simple « sac de données ». Les méthodes du Store sont faites pour cela.
  • Un seul Store énorme pour toute l’application. Faites de petits Stores, par feature, et composez-les avec des features. C’est plus facile à tester et à charger en lazy.
  • effect() dans les composants pour synchroniser des Stores entre eux. Préférez withComputed ou withLinkedState (voir effect()).

Aide-mémoire

signalStore(config?, ...features)        config : { providedIn: 'root' } ou rien (→ providers: [Store])
withState({ ... })                       état → Signals : store.x()
withComputed(store => ({ y: computed(...) }))
withMethods((store, dep = inject(X)) => ({ ... }))   le seul endroit où l’on modifie l’état
withHooks({ onInit(store) {}, onDestroy(store) {} })
withProps / withLinkedState              propriétés hors état / état lié (comme linkedSignal)
signalStoreFeature(...features)          morceaux réutilisables : withRequestStatus(), withPagination()
patchState(store, partial | updater, ...) plusieurs updaters en un seul appel
getState(store) / watchState(store, fn)   instantané / observateur synchrone de l’état
rxMethod<T>(pipe(...))                   from @ngrx/signals/rxjs-interop — accepte une valeur, un Signal ou un Observable
withEntities<T>() + setAllEntities, addEntity, updateEntity, removeEntity, upsertEntity  (@ngrx/signals/entities)
signalState({ ... })                     état local dans un composant, sans Store
unprotected(store)                       de @ngrx/signals/testing — définir l’état directement dans les tests

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