AngularAtlaséd. 22.2

Chap. 15Temps réel et hors ligne67 / 75

Offline-first : Service Worker et cache

Le Service Worker d’Angular : stratégies de cache, annonce des nouvelles versions et file d’attente pour les modifications faites hors ligne.

stable ~0 min de lecture Documentation officielle

Une application web a d’habitude besoin du réseau pour tout, y compris pour s’afficher. Un Service Worker se place entre l’application et le réseau : il peut répondre depuis un cache, essayer le réseau avec une limite de temps, ou les deux. Bien fait, l’application s’ouvre instantanément dans un train lent et continue de fonctionner dans un tunnel.

Angular a son propre Service Worker, piloté par un fichier de configuration plutôt que par du code. Cette page couvre les deux moitiés : la lecture (ce que vous affichez quand le réseau est lent ou absent) et l’écriture (ce que vous faites des modifications faites hors ligne).

Pourquoi c’est important
Hors ligne n’est pas un cas rare : ascenseurs, trains, Wi-Fi d’hôtel, une seule barre de réseau. Et un réseau lent est pire que pas de réseau : la requête n’échoue pas, elle reste suspendue. Un timeout qui se rabat sur le cache transforme un spinner de 30 secondes en réponse en trois.

L’activer

ng add @angular/pwa ajoute le paquet @angular/service-worker, un manifeste, des icônes, un ngsw-config.json et provideServiceWorker(). Le worker ne tourne que dans les builds de production et en HTTPS (localhost est l’exception). registerWhenStable:30000 l’enregistre une fois l’application stable, ou au plus tard après 30 secondes, pour que ses téléchargements ne concurrencent pas le premier rendu.

ng add @angular/pwa
# ajoute @angular/service-worker, ngsw-config.json, un manifeste, des icônes et provideServiceWorker()
ng build   # le worker ne tourne que dans les builds de production, en HTTPS (ou sur localhost)

Ce qui est mis en cache : assetGroups et dataGroups

Les assetGroups mettent en cache les fichiers de l’application. prefetch télécharge tout à l’installation — le shell : index.html, JS, CSS — pour que l’application s’ouvre hors ligne. lazy ne met un fichier en cache que la première fois qu’il est demandé : images, polices, pages rarement visitées. Les fichiers avec un hash dans leur nom ne deviennent jamais obsolètes ; le worker vérifie s’il existe une nouvelle version à chaque navigation.

Les dataGroups mettent en cache les réponses de votre API, avec l’une de deux stratégies. freshness interroge d’abord le réseau et se rabat sur le cache si le réseau échoue ou est plus lent que timeout : adapté aux données qui changent (prix, stock, notifications). performance répond d’abord depuis le cache et ne va sur le réseau que s’il n’y a pas de copie ; avec refreshAhead, une copie qui arrive aussi près de maxAge est toujours affichée, mais rafraîchie en arrière-plan avant d’expirer : adapté aux données qui changent rarement (un catalogue, des réglages, des traductions).

{
  "$schema": "./node_modules/@angular/service-worker/config/schema.json",
  "index": "/index.html",
  "assetGroups": [
    {
      "name": "app-shell",
      "installMode": "prefetch",
      "resources": { "files": ["/favicon.ico", "/index.html", "/*.css", "/*.js"] }
    },
    {
      "name": "assets",
      "installMode": "lazy",
      "updateMode": "prefetch",
      "resources": { "files": ["/assets/**", "/*.(svg|png|webp|woff2)"] }
    }
  ],
  "dataGroups": [
    {
      "name": "prices",
      "urls": ["/api/prices/**"],
      "cacheConfig": { "strategy": "freshness", "timeout": "3s", "maxSize": 50, "maxAge": "1h" }
    },
    {
      "name": "catalogue",
      "urls": ["/api/products/**"],
      "cacheConfig": {
        "strategy": "performance",
        "maxSize": 200,
        "maxAge": "7d",
        "refreshAhead": "1d"
      }
    }
  ]
}
LiveLa même requête : trois stratégies, trois réseaux
Stratégie
Réseau
Dans le cache
  1. 0 ms requête envoyée au réseau
  2. 3000 ms timeout après 3 s → réponse depuis le cache
  3. 6000 ms le réseau répond en retard : le cache est mis à jour pour la prochaine fois
contenu après
3000 ms
source
cache
âge des données
10 minutes
Choisissez un réseau lent : sans worker vous attendez six secondes, avec freshness trois, avec performance quelques millisecondes. Passez ensuite hors ligne avec un cache vide.

Attention à maxAge : c’est une limite stricte, pas une indication. Les entrées plus anciennes sont jetées, même sans réseau — une copie expirée n’est jamais affichée, hors ligne ou non. Choisissez-le selon les données les plus anciennes que vous accepteriez encore d’afficher.

Nouvelles versions : demander, ne pas changer le code sous les pieds de l’utilisateur

Quand vous déployez, le worker le remarque à la navigation suivante et télécharge la nouvelle version en arrière-plan. L’onglet ouvert continue d’exécuter l’ancienne — mélanger deux versions est ce qui casse les applications. SwUpdate.versionUpdates émet VERSION_READY quand la nouvelle version est complète ; affichez un message et rechargez quand l’utilisateur est d’accord. unrecoverable signifie que les fichiers de la version en cours ne sont plus sur le serveur : rechargez. Un onglet qui reste ouvert des jours devrait aussi appeler checkForUpdate() de temps en temps.

import { ApplicationRef, Component, DestroyRef, inject, signal } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { SwUpdate, type VersionReadyEvent } from '@angular/service-worker';
import { filter, interval } from 'rxjs';

@Component({
  selector: 'app-update-banner',
  template: `
    @if (ready()) {
      <div role="status">
        Une nouvelle version est prête.
        <button type="button" (click)="reload()">Recharger</button>
      </div>
    }
  `,
})
export class UpdateBanner {
  private readonly updates = inject(SwUpdate);
  protected readonly ready = signal(false);

  constructor() {
    if (!this.updates.isEnabled) return; // mode dev, ou un navigateur sans Service Worker
    const destroyRef = inject(DestroyRef);

    this.updates.versionUpdates
      .pipe(
        filter((e): e is VersionReadyEvent => e.type === 'VERSION_READY'),
        takeUntilDestroyed(destroyRef),
      )
      .subscribe(() => this.ready.set(true)); // demander ; ne pas recharger au milieu d’un formulaire

    // les fichiers de la version en cours ont disparu du serveur : seul un rechargement s’en sort
    this.updates.unrecoverable
      .pipe(takeUntilDestroyed(destroyRef))
      .subscribe(() => document.location.reload());

    // un onglet qui reste ouvert des jours doit aussi vérifier de temps en temps
    const appRef = inject(ApplicationRef);
    void appRef.whenStable().then(() =>
      interval(6 * 60 * 60 * 1000)
        .pipe(takeUntilDestroyed(destroyRef))
        .subscribe(() => void this.updates.checkForUpdate()),
    );
  }

  protected reload(): void {
    document.location.reload(); // la nouvelle version démarre au prochain chargement de la page
  }
}
Le cache devant le cache
ngsw.json et ngsw-worker.js ne doivent jamais être mis en cache longtemps par votre CDN ou votre serveur (Cache-Control: no-cache). Si le CDN garde un vieux ngsw.json, le worker n’apprend jamais qu’une nouvelle version existe et les utilisateurs restent sur l’ancienne tant que vit le cache du CDN. Les fichiers avec un hash dans leur nom, en revanche, peuvent être immutable.

Écrire hors ligne : une outbox

Le Service Worker met en cache les lectures ; il ne garde pas vos requêtes POST pour plus tard. Les modifications faites hors ligne vont dans une outbox : elles sont enregistrées localement, affichées comme en attente et partent une par une, dans l’ordre, quand la connexion revient.

LiveDes notes enregistrées hors ligne
en ligne votre vraie connexion : en ligne
  • Les notes enregistrées apparaissent ici, avec leur état de synchronisation.
en attente
0
synchronisées
0
Passez hors ligne, enregistrez deux notes, puis revenez en ligne : elles partent dans l’ordre. La démo réagit aussi à votre vraie connexion — coupez puis rallumez le Wi-Fi.

Trois détails rendent une outbox fiable. Elle est persistée (ici dans localStorage ; IndexedDB pour de grosses données ou si un worker doit la lire), donc un rechargement ne perd rien. Elle envoie un élément à la fois, donc le serveur reçoit les modifications dans l’ordre où elles ont été faites. Et chaque élément porte une Idempotency-Key générée au moment de l’enregistrement, pour qu’un envoi à moitié réussi ne crée pas de doublon — la même idée que pour les mises à jour optimistes. Pour navigator.onLine, seul false est certain : true veut dire qu’il y a un réseau, pas qu’il atteint Internet.

import { HttpClient, HttpContext } from '@angular/common/http';
import { DOCUMENT, Service, computed, effect, inject, signal, untracked } from '@angular/core';
import { firstValueFrom } from 'rxjs';
import { IDEMPOTENCY_KEY } from './idempotency'; // voir la page sur les mises à jour optimistes
import { injectOnline } from './online';

interface Pending {
  key: string; // Idempotency-Key, générée UNE SEULE FOIS quand l’utilisateur enregistre
  url: string;
  body: unknown;
}

const STORAGE = 'outbox';

@Service()
export class Outbox {
  private readonly http = inject(HttpClient);
  private readonly storage = inject(DOCUMENT).defaultView?.localStorage; // undefined pendant le SSR
  readonly online = injectOnline();
  readonly items = signal<readonly Pending[]>(JSON.parse(this.storage?.getItem(STORAGE) ?? '[]'));
  readonly waiting = computed(() => this.items().length);
  private flushing = false;

  constructor() {
    // survit à un rechargement (IndexedDB si les données sont volumineuses ou qu’un worker doit les lire)
    effect(() => this.storage?.setItem(STORAGE, JSON.stringify(this.items())));
    effect(() => {
      if (this.online()) untracked(() => void this.flush());
    });
  }

  save(url: string, body: unknown): void {
    this.items.update((list) => [...list, { key: crypto.randomUUID(), url, body }]);
    if (this.online()) void this.flush();
  }

  /** Une à la fois, dans l’ordre. Un échec arrête la file ; le prochain "online" la relance. */
  private async flush(): Promise<void> {
    if (this.flushing) return;
    this.flushing = true;
    try {
      while (this.online() && this.items().length) {
        const [next] = this.items();
        await firstValueFrom(
          this.http.post(next.url, next.body, {
            context: new HttpContext().set(IDEMPOTENCY_KEY, next.key),
          }),
        );
        this.items.update((list) => list.slice(1));
      }
    } catch {
      // toujours hors ligne, ou le serveur est en panne : l’élément reste en tête de file
    } finally {
      this.flushing = false;
    }
  }
}

Quand OUI, quand NON

Quand OUI
  • Les applications utilisées sur le terrain ou en déplacement : inspections, livraisons, visites commerciales.
  • Le contenu qu’on relit : documentation, articles, catalogues.
  • Les formulaires qui ne doivent pas perdre de données quand le réseau tombe.
Quand NON
  • Les données dangereuses quand elles sont périmées : trading, dispatch en direct, informations d’urgence.
  • Les outils internes toujours utilisés sur le réseau rapide du bureau : la complexité en plus rapporte peu.
  • Les applications où vous ne pouvez pas investir dans l’invalidation du cache et les tests : mettez au moins en cache le shell seul.

Pièges fréquents

  • Un CDN qui met ngsw.json en cache. Les utilisateurs restent sur l’ancienne version longtemps après le déploiement.
  • performance pour des données qui doivent être fraîches. Un prix d’hier, affiché instantanément et en toute confiance.
  • Recharger automatiquement quand une version est prête. L’utilisateur perd le formulaire qu’il remplissait.
  • Faire confiance à navigator.onLine === true. Le Wi-Fi d’hôtel avec page de connexion dit aussi « en ligne ».
  • Une outbox seulement en mémoire, ou sans clés d’idempotence. La première perd des données au rechargement, la seconde crée des doublons au nouvel essai.

Aide-mémoire

ng add @angular/pwa              worker + manifeste + ngsw-config.json (builds de production uniquement)
registrationStrategy             'registerWhenStable:30000' : après le premier rendu
assetGroups prefetch             le shell de l’app : index.html, JS, CSS, disponibles hors ligne
assetGroups lazy                 images, polices : mises en cache à la première utilisation
dataGroups freshness + timeout   le réseau d’abord ; plus lent que timeout → réponse depuis le cache
dataGroups performance           le cache d’abord ; refreshAhead rafraîchit en arrière-plan avant maxAge
maxAge                           limite stricte : les entrées plus anciennes sont jetées, même hors ligne
SwUpdate.versionUpdates          VERSION_READY → demander à l’utilisateur, recharger au clic
SwUpdate.unrecoverable           les fichiers de l’ancienne version ont disparu → recharger
checkForUpdate()                 périodiquement, pour les onglets ouverts pendant des jours
CDN / cache HTTP                 ngsw.json et ngsw-worker.js : no-cache, jamais immutable
navigator.onLine                 false est certain, true veut seulement dire « il y a un réseau »
écritures hors ligne             outbox : ordonnée, persistée, chacune avec une Idempotency-Key

Chap. 15 · 0 sur 3 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. WebSockets : un temps réel qui résiste au réseau
  2. Mises à jour optimistes et rollback
  3. Offline-first : Service Worker et cache
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