AngularAtlaséd. 22.2

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

RxJS avancé : opérateurs d’ordre supérieur

switchMap, mergeMap, concatMap et exhaustMap sur un diagramme interactif, plus retry avec backoff, shareReplay et comment éviter les fuites.

architecture ~0 min de lecture Documentation officielle

Les signals ont repris l’état, mais les événements dans le temps restent le terrain de RxJS : frappes au clavier, clics, requêtes qui se chevauchent, retries, websockets. La partie la plus importante, et celle que l’on rate le plus souvent, ce sont les opérateurs d’ordre supérieur. Chaque valeur démarre un nouvel Observable, généralement une requête HTTP. La vraie question est : que faites-vous quand la valeur suivante arrive alors que la requête précédente est encore en cours ?

Il y a quatre réponses : annuler, exécuter en parallèle, mettre en file ou ignorer. Chacune a son opérateur. Si vous choisissez mal, vous obtenez un bug de race condition : d’anciens résultats qui écrasent les nouveaux, des commandes en double, des enregistrements appliqués dans le mauvais ordre.

Pourquoi c’est important
« La recherche affiche parfois les résultats du texte précédent » ou « la commande a été envoyée deux fois » viennent presque toujours d’un mergeMap là où il fallait un switchMap ou un exhaustMap. La différence ne se voit pas dans des tests rapides, seulement sur un réseau lent. Vous pouvez reproduire exactement cela dans la démo ci-dessous.

Le problème : un Observable d’Observables

Un map qui renvoie une requête produit un Observable<Observable<T>> : un flux de requêtes auxquelles personne ne s’est abonné. Les opérateurs *Map s’abonnent eux-mêmes aux flux internes et les aplatissent en un seul flux de résultats. La seule différence entre eux est la politique de chevauchement.

// « Pour chaque valeur, démarrer un Observable » — la question est de savoir ce que vous faites quand la valeur suivante arrive
// alors que la précédente tourne encore.
clicks$.pipe(map((id) => http.get(`/api/items/${id}`)));       // ✗ Observable<Observable<Item>>
clicks$.pipe(switchMap((id) => http.get(`/api/items/${id}`))); // ✓ Observable<Item> — plus une politique

Les quatre opérateurs

OpérateurUne nouvelle valeur arrive pendant que l’ancienne s’exécuteExemple typique
switchMapannule l’ancienne (désabonnement → HttpClient annule la requête)typeahead, paramètres de route
mergeMaples exécute en parallèle ; l’ordre des résultats n’est pas garantienvois de fichiers indépendants
concatMapla met en file ; exécution une par une, dans l’ordreenregistrement automatique, commandes ordonnées
exhaustMapl’ignore jusqu’à la fin de l’actuelleenvoi de formulaire, connexion
// Typeahead : seule la dernière requête compte ; les anciennes sont ANNULÉES (HttpClient annule aussi le XHR/fetch)
readonly results = toSignal(
  this.query$.pipe(
    debounceTime(300),
    distinctUntilChanged(),
    switchMap((q) => this.api.search(q).pipe(catchError(() => of([])))),
  ),
  { initialValue: [] },
);
LiveDiagramme en billes interactif (vrais opérateurs RxJS)

switchMap : un nouveau clic ANNULE la requête en cours. Idéal pour la recherche (typeahead) et les paramètres de route.

clics (externe)
requêtes (interne)
résultat (sortie)
émis: 0livré: 0annulé: 0
Choisissez un opérateur et appuyez plusieurs fois sur « Émettre », plus vite que la durée de la requête. switchMap coupe les anciennes barres (✕), mergeMap les superpose, concatMap les place l’une après l’autre, exhaustMap ignore celles faites pendant une requête.

Où placer catchError

Une erreur termine le flux dans lequel elle se produit. Si catchError se trouve après switchMap, la première requête en échec ferme tout le flux : la recherche ne répond plus, sans aucune erreur dans la console. Placez-le à l’intérieur de la projection interne. Ainsi, seule cette requête est remplacée par une valeur de repli, et le flux externe continue.

// ✗ catchError APRÈS switchMap : la première erreur termine le flux externe — la recherche « meurt »
query$.pipe(
  switchMap((q) => api.search(q)),
  catchError(() => of([])),
);

// ✓ catchError DANS la projection : seule cette requête échoue, le flux externe reste vivant
query$.pipe(
  switchMap((q) => api.search(q).pipe(catchError(() => of([])))),
);

Retry avec backoff exponentiel

retry({ count, delay }) se réabonne à la source après une erreur. La fonction delay reçoit l’erreur et le numéro de la tentative, et renvoie un Observable qui dit quand réessayer. Doubler la pause à chaque étape (300, 600, 1200 ms…) évite de marteler le serveur quand il a des problèmes. Relancez l’erreur pour les codes qui ne se corrigeront pas d’eux-mêmes (401, 404). Pour HTTP, le retry vit généralement dans un interceptor (voir HttpClient & interceptors). Pour une connexion qui doit survivre aux redémarrages du serveur — avec du jitter, pour que mille clients ne reviennent pas dans la même seconde —, voir WebSockets.

import { retry, timer } from 'rxjs';

http.get('/api/report').pipe(
  retry({
    count: 4,                                                // 4 retries au maximum
    delay: (error, retryCount) => {
      if (error.status === 401 || error.status === 404) throw error; // ne pas réessayer les erreurs « définitives »
      return timer(300 * 2 ** (retryCount - 1));              // 300, 600, 1200, 2400 ms
    },
    resetOnSuccess: true,
  }),
);
Liveretry avec backoff, sur une source qui échoue
  1. Appuyez sur le bouton.
Définissez combien d’échecs le serveur subit et combien de tentatives vous autorisez. Le journal montre les pauses croissantes. Si les échecs dépassent count, vous obtenez l’erreur finale.

share et shareReplay

Un Observable « cold », comme http.get, s’exécute à chaque subscribe. Trois abonnés, c’est trois requêtes. share() fait partager une seule exécution aux abonnés simultanés. shareReplay conserve en plus la dernière valeur pour ceux qui arrivent plus tard : il fonctionne donc comme un cache. Le piège est un simple shareReplay(1) : il a refCount: false, donc il ne se désabonne jamais de la source. Sur un flux qui ne se termine jamais (un websocket, un interval), la source continue de tourner après le départ de tous les abonnés.

Un détail subtil avec refCount: true : le cache n’est conservé que si la source a eu le temps de se terminer. Si tous les abonnés partent avant complete (par exemple firstValueFrom, take(1), ou un composant détruit pendant la requête), shareReplay se désabonne de la source et repart de zéro avec l’abonné suivant.

// Sans partage : chaque subscribe = une exécution de plus (une requête de plus)
const user$ = http.get<User>('/api/me');
user$.subscribe(); user$.subscribe();                 // 2 requêtes

// share() : les abonnés SIMULTANÉS partagent une exécution ; après complete, il se réinitialise
const shared$ = http.get<User>('/api/me').pipe(share());

// shareReplay : conserve la dernière valeur pour les abonnés TARDIFS (cache)
const cached$ = http.get<User>('/api/me').pipe(shareReplay({ bufferSize: 1, refCount: true }));
// refCount: true → quand le dernier abonné part, il se désabonne aussi de la source.
// shareReplay(1) (refCount: false) sur une source qui ne se termine jamais = la source tourne indéfiniment.
LiveCombien d’exécutions ? Et qui reste abonné ?
3 abonnés simultanés + 1 abonné tardif à une « requête » de 400 ms ; puis un flux infini, dont tous se désabonnent.
Variante Exécutions de la requête Abonné tardif Flux infini après désabonnement
Appuyez sur « Lancer le scénario » (environ 2 s).
Le scénario s’exécute avec le vrai RxJS : les mêmes 3 abonnés simultanés et 1 tardif, puis un flux infini dont tout le monde se désabonne. Seul shareReplay(1) laisse la source tourner. Les sources de la démo sont arrêtées à la fin.

Combiner des flux

combineLatest émet dès qu’une source change, une fois que chacune a émis au moins une fois. Il convient aux filtres combinés. withLatestFrom n’émet qu’à partir de la source principale et prend la valeur courante des autres (« au clic, enregistrer le formulaire actuel »). forkJoin attend que toutes se terminent et n’émet qu’une fois : c’est un Promise.all pour les requêtes.

// combineLatest : émet à chaque changement, une fois que TOUTES ont émis une fois — filtres combinés
combineLatest([category$, sort$]).pipe(switchMap(([c, s]) => api.list(c, s)));

// withLatestFrom : n’émet qu’à partir de la source principale, en prenant la dernière valeur des autres
save$.pipe(withLatestFrom(form$), concatMap(([, value]) => api.save(value)));

// forkJoin : attend que TOUTES se terminent, une seule émission — requêtes parallèles indépendantes
forkJoin({ user: api.me(), settings: api.settings() });

// zip : associe la n-ième valeur de chacune — rarement utile dans une UI

Aucun abonnement oublié

Chaque subscribe manuel dans un composant doit être fermé. takeUntilDestroyed() le fait automatiquement à la destruction du composant. Pour les valeurs affichées dans le template, toSignal() est encore plus simple : il s’abonne et se désabonne tout seul (voir Signals ↔ RxJS).

import { takeUntilDestroyed } from '@angular/core/rxjs-interop';

export class Live {
  constructor() {
    interval(1000)
      .pipe(takeUntilDestroyed()) // dans un contexte d’injection ; sinon : takeUntilDestroyed(this.destroyRef)
      .subscribe((n) => this.tick.set(n));
  }
}
// Pour l’état affiché dans le template, toSignal(obs$) se désabonne tout seul (voir la page Signals ↔ RxJS).

Quand OUI, quand NON

RxJS est le bon outil
  • Événements dans le temps : debounce, throttle, fenêtres temporelles, combinaison de plusieurs sources.
  • Requêtes qui peuvent se chevaucher et demandent une politique : annulation, mise en file, ignorance.
  • Retries, timeouts, polling, websockets, server-sent events.
  • Flux de commandes ordonnées (enregistrement automatique, files hors ligne).
Signals / resource suffisent
  • État affiché dans l’UI : signal / computed (voir Signals).
  • Une requête qui dépend de paramètres : httpResource / rxResource annulent d’eux-mêmes la requête périmée (voir resource).
  • Une seule requête par clic, sans chevauchement possible : un simple subscribe ou une méthode async.
  • « Pour le rendre réactif » : un BehaviorSubject pour une valeur simple est désormais un signal().

Pièges fréquents

  • mergeMap pour la recherche. Les réponses arrivent dans n’importe quel ordre, et une ancienne peut écraser la nouvelle. Utilisez switchMap.
  • switchMap pour les enregistrements. Un enregistrement annulé à mi-chemin a peut-être déjà été traité par le serveur, ou non. Pour les écritures, utilisez concatMap ou exhaustMap. Plus de détails sur les écritures qui arrivent dans le désordre dans les mises à jour optimistes.
  • Subscribe dans subscribe. Vous perdez l’annulation et la gestion des erreurs. Tout « subscribe, puis un autre subscribe dans le callback » est un opérateur *Map.
  • catchError au niveau externe d’un flux qui doit rester vivant : il arrête tout à la première erreur.
  • shareReplay(1) sur des sources infinies : fuites de mémoire et de connexions. Utilisez { bufferSize: 1, refCount: true }. Comment les trouver : voir la page sur les fuites de mémoire.
  • Retry sur 4xx : vous réessayez indéfiniment une requête qui ne réussira jamais. Filtrez par statut.

Aide-mémoire

OPÉRATEUR     QUAND UNE NOUVELLE VALEUR ARRIVE PENDANT QUE L’ANCIENNE TOURNE  USAGE
switchMap     annuler l’ancienne                                              recherche, paramètres de route, « la dernière gagne »
mergeMap      les exécuter en parallèle (optionnel : concurrency)             opérations indépendantes, envois de fichiers
concatMap     la mettre en file, dans l’ordre                                 enregistrements ordonnés, files de commandes
exhaustMap    l’ignorer                                                       envoi de formulaire, connexion, rafraîchissement manuel

catchError    DANS la projection interne, pour que le flux externe reste vivant
retry         { count, delay: (err, n) => timer(base * 2 ** (n - 1)) } — ne pas réessayer les 4xx
share()       les abonnés simultanés partagent l’exécution
shareReplay   { bufferSize: 1, refCount: true } — cache + désabonnement correct
combineLatest filtres combinés · withLatestFrom « au clic, prendre la valeur courante » · forkJoin « toutes, une fois »
takeUntilDestroyed() / toSignal()   aucun abonnement oublié

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