AngularAtlaséd. 22.2

Chap. 12SSR et Hydration55 / 75

Hydration : event replay et incrémentale

Comment le navigateur reprend le HTML du serveur sans le re-rendre : Hydration complète, event replay, @defer (hydrate on …) et erreurs de mismatch.

v22stable ~0 min de lecture Documentation officielle

Quand la page arrive déjà rendue par le serveur (SSR ou prerender), le navigateur a déjà le DOM complet. Sans hydration, Angular démarrerait comme dans une application CSR : il jetterait ce DOM et le reconstruirait — un scintillement visible, un LCP dégradé, un décalage de mise en page. L’hydration signifie qu’Angular parcourt le DOM existant, le fait correspondre à l’arbre de composants et le réutilise : il branche les bindings et attache les listeners aux nœuds qui existent déjà.

Deux optimisations ont été construites par-dessus : l’event replay — les clics faits avant l’hydration ne sont pas perdus — et l’hydration incrémentale — des parties de la page restent du HTML statique, sans JS téléchargé, jusqu’à ce qu’on en ait vraiment besoin.

En bref
  • L’hydratation réutilise le HTML envoyé par le serveur au lieu de le recréer : la page ne clignote pas au démarrage.
  • L’event replay retient les clics faits avant le chargement du JavaScript et les rejoue ensuite.
  • L’hydratation incrémentale (@defer avec hydrate on …) laisse des parties de la page non interactives jusqu’à ce qu’on en ait besoin. Leur code n’est téléchargé qu’à ce moment-là.
  • Le HTML du serveur et celui du navigateur doivent être identiques. Sinon apparaissent des erreurs de non-correspondance, avec des codes NG05xx.
  • ngSkipHydration est la sortie de secours pour un composant qui modifie le DOM directement.
Ce qui a changé en v22
  • provideClientHydration() démarre l’hydration incrémentale par défaut, et active automatiquement l’event replay.
  • withIncrementalHydration() est déprécié (suppression prévue en v24) — retirez-le de la config, c’est déjà le comportement par défaut.
  • La nouvelle option de désactivation est withNoIncrementalHydration().

Ce que fait l’hydration, étape par étape

Sur le serveur, après le rendu, Angular annote le HTML pour pouvoir le « retrouver » plus tard : l’attribut ngh sur chaque hôte de composant (un index dans les données d’hydration), des commentaires d’ancrage pour @if/@for/@defer, un commentaire HTML nghm comme marqueur d’intégrité, jsaction sur les éléments qui ont des listeners, et un <script id="ng-state"> avec les données d’hydration (__nghData__) et les réponses HTTP du transfer cache.

@Component({
  selector: 'app-product-card',
  template: `
    <h2>{{ product().name }}</h2>
    @if (product().stock > 0) {
      <button (click)="addToCart()">Ajouter au panier</button>
    }
  `,
})
export class ProductCard {
  readonly product = input.required<Product>();
  addToCart() { /* ... */ }
}

Dans le navigateur, au bootstrap, Angular ne crée plus de nœuds : il cherche les nœuds existants, grâce aux annotations. Pour chaque nœud, il vérifie le type et la balise (pas le texte) et attache les listeners. Un texte différent n’est pas une erreur — il est simplement écrasé à la première Change Detection.

Une fois l’application stable (aucune requête HTTP ni PendingTasks en cours), vient l’étape finale : Angular supprime les nœuds du serveur que personne n’a réclamés, rejoue les événements de la file et désactive le transfer cache. C’est pourquoi une application qui ne devient pas stable a des effets visibles même quand le DOM a été correctement réutilisé.

Sans hydrationHydration complèteHydration incrémentale
Le DOM du serveurjeté et recrééréutiliséréutilisé
JS nécessaire au démarragetouttoutseulement ce qui est en dehors des blocs @defer (hydrate …)
Quand un bloc devient interactifaprès le nouveau renduau bootstrap, la page entière d’un coupau trigger du bloc
Clics avant le JSperdusrejoués seulement avec withEventReplay()rejoués ; un clic sur un bloc déshydraté l’hydrate

provideClientHydration et ses options

Si vous avez ajouté le SSR avec ng add @angular/ssr, le provider est déjà dans app.config.ts. Sans arguments, en v22, vous obtenez l’ensemble recommandé :

import { ApplicationConfig } from '@angular/core';
import { provideClientHydration } from '@angular/platform-browser';

export const appConfig: ApplicationConfig = {
  providers: [
    // v22, sans arguments :
    //  ✓ hydration non destructive (réutilise le DOM du serveur)
    //  ✓ HTTP transfer cache (les GET/HEAD faits sur le serveur ne sont pas répétés dans le navigateur)
    //  ✓ hydration incrémentale (@defer avec hydrate …) — inclut l’event replay
    //  ✓ en mode dev : provideStabilityDebugging()
    provideClientHydration(),
  ],
};
// app.config.server.ts fait mergeApplicationConfig(appConfig, serverConfig),
// le provider atteint donc aussi le serveur — obligatoire, sinon NG0505 apparaît.
FonctionCe qu’elle faitEn v22
provideClientHydration()hydration non destructive + HTTP transfer cache + hydration incrémentale (avec event replay)l’ensemble par défaut
withNoIncrementalHydration()désactive l’hydration incrémentale (et, avec elle, l’event replay)nouveau en v22
withEventReplay()event replay sans hydration incrémentaleinclus par défaut ; explicite seulement avec la désactivation ci-dessus
withI18nSupport()hydrate aussi les composants avec des blocs i18n ; sans elle, ils sont rendus de zérooptionnel
withHttpTransferCacheOptions({…})configure quelles réponses se retrouvent dans le HTML : filter, includeHeaders, includePostRequests, includeRequestsWithAuthHeaders…optionnel
withNoHttpTransferCache()désactive le transfer cache : chaque GET est exécuté deux foisoptionnel
withIncrementalHydration()servait à activer l’hydration incrémentaledépréciée depuis la v22 (suppression prévue en v24)

Le provider doit aussi exister dans la config serveur — le app.config.server.ts généré par la CLI fait mergeApplicationConfig(appConfig, serverConfig), il y est donc automatiquement. S’il manque, le serveur n’annote pas le HTML, et dans le navigateur la page est rendue de zéro (en mode dev, avec l’avertissement NG0505). Les combinaisons contradictoires (par exemple withNoHttpTransferCache() avec withHttpTransferCacheOptions()) lèvent l’erreur NG05001 en mode dev.

Event replay

Le problème : le HTML du serveur est visible bien avant l’arrivée du JS (dans la timeline de la page SSR, c’est la bande jaune). L’utilisateur voit un bouton et appuie dessus — mais le bouton n’a pas encore de listener.

Le mécanisme repose sur JSAction, une bibliothèque de délégation d’événements intégrée à Angular :

  1. Marquage — pendant la sérialisation, le serveur place jsaction="click:;" sur chaque élément qui a un listener dans le template.
  2. Capture — un petit script inline, injecté juste après <body> (l’event contract), écoute au niveau de body les types d’événements utilisés dans la page et les garde en mémoire.
  3. Relecture — après l’hydration, quand l’application devient stable, Angular envoie les événements en file, dans l’ordre, aux vrais handlers.

Cela fonctionne pour les événements natifs du navigateur (click, mouseover, focusin, etc.). Avec l’hydration incrémentale, le contract reste actif aussi après l’hydration, ce qui lui permet d’attraper les événements dans les blocs encore déshydratés.

LiveEvent replay : clics avant l’hydration (simulation)
magazin.ro/produse/42hydraté · interactif
Casque sans fil Aria X2
349 RON
Panier : 0Favoris : 0
main.js · exécuté
File de l’event contract (en mémoire)
vide
  • Appuyez sur « Recharger la page », puis cliquez sur les boutons tant que la barre JS n’est pas pleine.
Avant l’hydration : 0 clics · rejoués 0 · perdus 0
Appuyez sur « Recharger la page », puis cliquez plusieurs fois tant que la barre main.js n’est pas pleine. Avec le replay activé, les compteurs « rattrapent leur retard » après l’hydration ; désactivez-le et recommencez : les clics sont perdus.
Retour visuel pendant la fenêtre d’hydration

Le replay sauvegarde l’action, mais le bouton ne réagit pas visuellement avant l’hydration. Les états CSS (:hover, :active, :focus-visible) fonctionnent sans JS — utilisez-les, pour que l’utilisateur sache que le clic a été « pris en compte ».

Hydration incrémentale avec @defer

Un @defer ordinaire ne rend que le @placeholder sur le serveur : les triggers ne s’exécutent pas sur le serveur. Un trigger hydrate change cela : le serveur charge les dépendances du bloc et rend le contenu principal. Dans le navigateur, le bloc reste déshydraté — le HTML est visible, mais le chunk JS des composants qu’il contient n’est même pas téléchargé — jusqu’au déclenchement du trigger. Alors Angular télécharge le chunk, hydrate le bloc et rejoue les événements en file.

Deux conséquences pratiques. Première : vous pouvez placer un @defer au-dessus de la ligne de flottaison sans décalage de mise en page, car le contenu final est déjà à l’écran, pas le placeholder. Seconde : un événement sur un élément avec listener dans un bloc déshydraté déclenche son hydration, quel que soit le trigger déclaré — le clic n’est pas perdu. L’exception est hydrate never : son sous-arbre n’est pas du tout annoté, il n’y a donc rien à rejouer.

<app-header />                                    <!-- pas de @defer : hydraté au bootstrap -->

@defer (hydrate on interaction) {
  <app-gallery [images]="product().images" />     <!-- le JS de la galerie : seulement au premier clic -->
} @placeholder {
  <div class="gallery-skeleton"></div>            <!-- utilisé seulement lors d’une navigation côté client -->
}

@defer (hydrate on hover) {
  <app-recommendations [productId]="id()" />
} @placeholder {
  <div class="reco-skeleton"></div>
}

@defer (hydrate on idle) {
  <app-support-chat />
} @placeholder {
  <button>Chat</button>
}

@defer (hydrate on viewport) {
  <app-reviews [productId]="id()" />              <!-- sous la ligne de flottaison : JS quand il entre à l’écran -->
} @placeholder {
  <div class="reviews-skeleton"></div>
}

@defer (hydrate never) {
  <app-footer />                                  <!-- HTML statique pour toujours -->
}
LiveÎlots hydratés à la demande (simulation)
JS téléchargé : 0 KB sur 218 KB · 0/6 blocs actifs
En-têtepas de @defer (main.js)déshydraté48 KB, non téléchargés
Galeriehydrate on interactiondéshydraté36 KB, non téléchargés
image 1 / 4
Recommandationshydrate on hoverdéshydraté22 KB, non téléchargés
Étui Aria · 49 RON
Chat d’assistancehydrate on idledéshydraté30 KB, non téléchargés
Des questions ?
Description du produit, caractéristiques, livraison…
↓ faites défiler jusqu’aux avis ↓
Avishydrate on viewportdéshydraté64 KB, non téléchargés
★★★★☆ · 3 avis affichés
Pied de pagehydrate neverstatique, sans JS0 KB (serait 18)
Contact · CGV · Retours · © 2026
  • La simulation démarre quand la démo entre dans la zone visible (ou appuyez sur « Recharger »).
Cliquez sur la galerie, survolez les recommandations, faites défiler jusqu’aux avis. Observez quel bloc s’hydrate, pourquoi, et combien de KB cela coûte ; puis passez sur « Complète » et comparez le JS téléchargé au démarrage.

Les triggers d’hydration

@defer (hydrate on idle) { … }                    <!-- requestIdleCallback -->
@defer (hydrate on idle(500)) { … }               <!-- idle, mais au plus tard après 500 ms -->
@defer (hydrate on viewport) { … }                <!-- le contenu du bloc entre à l’écran -->
@defer (hydrate on viewport({ rootMargin: '200px' })) { … }  <!-- options littérales uniquement -->
@defer (hydrate on interaction) { … }             <!-- click / keydown dans le bloc -->
@defer (hydrate on hover) { … }                   <!-- mouseover / focusin dans le bloc -->
@defer (hydrate on immediate) { … }               <!-- juste après le reste de la page -->
@defer (hydrate on timer(2s)) { … }               <!-- ms ou s -->
@defer (hydrate when cartReady()) { … }           <!-- condition issue du composant parent -->
@defer (hydrate never) { … }                      <!-- jamais ; aucun autre trigger hydrate -->
@defer (hydrate on viewport; hydrate on timer(5s)) { … }  <!-- plusieurs = n’importe lequel (OU) -->

<!-- ✗ erreurs de compilation -->
@defer (hydrate on interaction(btn)) { … }        <!-- interaction/hover n’acceptent pas de référence -->
@defer (hydrate on viewport({ trigger: el })) { … }
@defer (hydrate never; hydrate on idle) { … }     <!-- never exclut tout autre trigger hydrate -->
TriggerSe déclenche quandParamètres
hydrate on idlele navigateur est disponible (requestIdleCallback)optionnel, un timeout : idle(500)
hydrate on viewportle contenu du bloc entre à l’écranoptionnel, options IntersectionObserver littérales ; pas de référence à un autre élément
hydrate on interactionclick ou keydown dans le blocaucun
hydrate on hovermouseover ou focusin dans le blocaucun
hydrate on immediatedès que le reste de la page a fini de s’afficher—
hydrate on timer(…)après un intervalleobligatoire, en ms ou s
hydrate when exprl’expression devient truthyne fonctionne que sur le bloc déshydraté le plus externe
hydrate neverjamais : HTML statique, sous-arbre entier comprisne peut pas être combiné avec d’autres triggers hydrate

Plusieurs triggers sur le même bloc se combinent avec « n’importe lequel » (OU). hydrate when a une limite : l’expression appartient au composant parent, qui doit lui-même être hydraté pour que l’expression soit évaluée.

Blocs imbriqués

L’arbre de composants est hiérarchique : un enfant ne peut pas être actif sans son parent. Si le trigger se déclenche sur un bloc imbriqué, Angular hydrate la chaîne de haut en bas — d’abord le bloc déshydraté le plus externe, puis un par un jusqu’à celui qui l’a demandé.

@defer (hydrate on interaction) {
  <app-product-tabs />                <!-- 1. le parent est hydraté en premier… -->
  @defer (hydrate on hover) {
    <app-size-guide />                <!-- 2. …puis l’enfant, même si le survol était sur lui -->
  }
}

Les triggers hydrate en CSR et lors de la navigation

L’hydration est une optimisation du chargement initial du contenu rendu par le serveur. Quand l’utilisateur arrive sur la même page par une navigation côté client (un routerLink), ou quand l’application n’a pas de SSR du tout, les triggers hydrate sont ignorés : le bloc se comporte comme un @defer ordinaire, avec son @placeholder et les triggers normaux. Si vous n’avez mis aucun trigger normal, le compilateur ajoute on idle.

@defer (on interaction; hydrate on viewport) {
  <app-reviews />
} @placeholder {
  <button>Afficher les avis</button>
}

<!--
  Chargement initial, page rendue par le serveur :
    → le serveur rend <app-reviews /> (pas le placeholder)
    → dans le navigateur, cela reste du HTML statique jusqu’à son entrée dans le viewport (hydrate on viewport)

  Navigation côté client (routerLink) ou application sans SSR :
    → hydrate on viewport est ignoré
    → le @placeholder est affiché, le bloc se charge au clic (on interaction)
    → sans trigger normal, le défaut aurait été on idle
-->
  • @placeholder reste nécessaire — il ne s’affiche pas au chargement initial avec SSR, mais il s’affiche lors de toute navigation côté client vers la page.
  • hydrate never signifie « jamais de JS au chargement initial ». Lors d’un rendu côté client ultérieur, le bloc télécharge quand même ses dépendances, avec le trigger normal.
  • Dans une application sans provideClientHydration() (ou avec withNoIncrementalHydration()), les blocs avec des triggers hydrate produisent l’avertissement NG0508 en mode dev.

Erreurs de correspondance (mismatch)

L’hydration part d’une promesse : le DOM dans le navigateur est exactement celui que le serveur a produit, commentaires et espaces compris. Tout ce qui rompt la promesse — le parseur HTML du navigateur, un script tiers, un CDN, une branche de template différente — aboutit à l’une de trois situations : une erreur, un avertissement ou, le plus sournois, aucune erreur mais un nouveau rendu visible.

CodeSignifieCause typique
NG0500le nœud trouvé n’est pas celui attendu (balise ou type différent) ou il est absentHTML invalide, manipulation directe du DOM, script tiers
NG0501des nœuds frères sont absentsnœuds supprimés en dehors d’Angular
NG0502le nœud n’est pas trouvé à la position attenduestructure modifiée après le serveur
NG0503nœuds projetés créés en dehors d’AngularcreateComponent avec des projectableNodes issus du DOM natif
NG0504ngSkipHydration sur un élément qui n’est pas un hôte de composantl’attribut placé sur un div
NG0505(avertissement) les annotations du serveur sont absentesprovideClientHydration() manque dans la config serveur
NG0506(avertissement) l’application ne devient pas stable en 10 sPendingTasks non terminées ; avec Zone.js, des timers dans la zone Angular
NG0507le HTML a été modifié après le serveurun CDN qui supprime les commentaires
LiveExplorateur de mismatch (simulation)
Votre code
<p class="promo">
  <div>Livraison gratuite aujourd’hui</div>
</p>
HTML envoyé par le serveur
<p class="promo"><div>Livraison gratuite aujourd’hui</div></p>
DOM dans le navigateur, avant l’hydration
<p class="promo"></p><div>Livraison gratuite aujourd’hui</div><p></p>
erreur · NG0500
Console
NG0500: During hydration Angular expected <div> but the node was not found.

Angular expected this DOM:

  <p class="promo">
    <div>…</div>  <-- AT THIS LOCATION
    …
  </p>

To fix this problem:
  * check the "PromoBanner" component for hydration-related issues
  * check to see if your template has valid HTML structure
  …

Ce qui se passe : Le serveur construit le DOM via des API, donc l’imbrication invalide survit jusqu’à la sérialisation. Le parseur HTML du navigateur, lui, ferme le <p> dès qu’il rencontre le <div>, et Angular ne retrouve plus le nœud attendu.

Correctif : HTML valide : <p> n’accepte que du contenu inline. Utilisez un <div class="promo"> comme parent, ou un <span> à l’intérieur.

Choisissez un motif : voyez le code, le HTML envoyé par le serveur, le DOM qu’Angular trouve dans le navigateur et le résultat. Les messages de la console sont cités (abrégés) d’après les sources d’Angular 22.
Des branches différentes sur le serveur et dans le navigateur

@if (isPlatformBrowser(…)) dans un template ne lève pas d’erreur, mais c’est l’une des erreurs les plus coûteuses : Angular jette le DOM du serveur pour le bloc et le reconstruit, avec un décalage de mise en page. Gardez un rendu identique sur les deux plateformes et déplacez l’initialisation réservée au navigateur dans afterNextRender — ou utilisez @defer, qui rend le placeholder sur le serveur. Les variantes sont sur la page Code compatible serveur.

ngSkipHydration : la sortie de secours

Pour un composant qui ne peut pas être corrigé immédiatement (typiquement une bibliothèque qui dessine directement dans le DOM, comme D3), l’attribut ngSkipHydration sur son hôte le sort de l’hydration : le composant et ses enfants sont détruits et rendus de nouveau, exactement comme sans hydration. Il ne fonctionne que sur les hôtes de composants (sinon NG0504), et placé sur le composant racine, il désactive l’hydration pour toute l’application.

<!-- sur l’hôte du composant qui manipule directement le DOM (p. ex. un graphique D3) -->
<app-sales-chart ngSkipHydration [data]="sales()" />

<!-- ✗ NG0504 : seulement sur les hôtes de composants, pas sur les éléments ordinaires -->
<div ngSkipHydration><app-sales-chart [data]="sales()" /></div>

Débogage

  • La console, en mode dev : une fois l’application stable, un résumé apparaît. Si vous ne le voyez pas, l’hydration n’a pas démarré (vérifiez NG0505).
  • Angular DevTools affiche l’état d’hydration de chaque composant et peut activer une surcouche sur les parties hydratées ; en cas d’erreur de mismatch, il met en évidence le composant fautif.
  • Code source vs Éléments : le code source est le HTML du serveur, Éléments est le DOM actuel. Les différences entre les deux (un tbody en plus, une bannière injectée) sont exactement les causes de NG0500.
  • Désactivez JavaScript dans les DevTools et rechargez : vous voyez exactement ce que l’utilisateur reçoit avant l’hydration.
  • NG0506 : provideStabilityDebugging() liste les tâches qui maintiennent l’application instable (avec Zone.js, importez aussi zone.js/plugins/task-tracking).
  • preserveWhitespaces doit avoir la même valeur dans tsconfig.app.json et tsconfig.server.json ; la valeur par défaut (false) est la recommandée.
Angular hydrated 14 component(s) and 212 node(s), 0 component(s) were skipped.
5 defer block(s) were configured to use incremental hydration.
Learn more at https://angular.dev/guide/hydration.

Quand OUI, quand NON

Utiliser
  • Hydration : dès que vous avez du SSR ou du prerender. Sans elle, le SSR perd la moitié de son intérêt (le DOM est reconstruit).
  • Event replay : activé par défaut ; il compte surtout sur les pages avec des actions rapides (panier, like, menu) et les réseaux lents.
  • hydrate on viewport pour tout ce qui est sous la ligne de flottaison : avis, produits similaires, un footer riche.
  • hydrate on interaction / hover pour les widgets lourds qui restent inactifs jusqu’au premier contact : galeries, filtres, sélecteurs de taille.
  • hydrate never pour du contenu vraiment statique : un article, des CGV, un footer sans logique.
Éviter / l’alternative
  • Applications sans SSR : les triggers hydrate ne font rien. Utilisez @defer avec des triggers normaux.
  • @defer (hydrate …) sur des composants de quelques KB : le chunk et la requête supplémentaires coûtent plus que ce que vous économisez.
  • hydrate never sur quelque chose qui a des listeners ou un état : cela ne fonctionnera jamais au chargement initial.
  • ngSkipHydration comme « correctif » permanent : corrigez la cause (HTML valide, afterNextRender, DOM manipulé via Angular).
  • Des branches isPlatformBrowser dans le template pour masquer les problèmes : l’alternative est @defer ou afterNextRender.

Pièges fréquents

Manipulation directe du DOM

nativeElement.appendChild, innerHTML, document.querySelector(…).remove() dans des composants qui s’exécutent aussi sur le serveur produisent des structures que l’hydration ne peut pas faire correspondre. Utilisez le template (@if, @for, bindings, Renderer2) et reportez le code strictement navigateur dans afterNextRender.

  • HTML invalide : un <div> dans un <p>, un <a> dans un <a>, une table sans <tbody>. Validez le template ; le navigateur « répare » le HTML autrement que le serveur ne l’a écrit.
  • Des valeurs différentes sur le serveur et le client (Date.now(), Math.random(), identifiants générés, fuseaux horaires) : pas d’erreur, mais le texte change sous les yeux de l’utilisateur. Transférez la valeur avec TransferState.
  • Des scripts tiers (analytics, consentement, chat) qui modifient le DOM avant l’hydration : chargez-les après, avec afterNextRender.
  • withEventReplay() et withIncrementalHydration() laissés « au cas où » en v22 : le premier est redondant, le second est déprécié. Un simple provideClientHydration() suffit.
  • Un @placeholder supprimé des blocs avec hydrate, parce que « de toute façon on ne le voit pas » : lors d’une navigation côté client, on le voit.
  • Des données personnelles dans le transfer cache : les réponses se retrouvent dans le HTML. Les requêtes avec Authorization/Cookie sont exclues par défaut — ne les incluez pas avec includeRequestsWithAuthHeaders sans raison ; utilisez filter ou transferCache: false.
  • Un CDN avec minification HTML qui supprime les commentaires → NG0507 sur chaque page.

Aide-mémoire

// ── Config (app.config.ts ; atteint aussi le serveur via mergeApplicationConfig)
provideClientHydration();                                     // v22 : DOM + transfer cache + incrémentale + replay
provideClientHydration(withNoIncrementalHydration(), withEventReplay());
provideClientHydration(withI18nSupport());
provideClientHydration(withHttpTransferCacheOptions({ filter, includeHeaders, includePostRequests }));
provideClientHydration(withNoHttpTransferCache());
http.get(url, { transferCache: false });                      // désactivation requête par requête

// ── Template
// @defer (hydrate on idle | idle(ms) | viewport | viewport({ rootMargin }) | interaction | hover | immediate | timer(2s))
// @defer (hydrate when cond)      @defer (hydrate never)      @defer (on idle; hydrate on viewport)
// <app-chart ngSkipHydration />   host: { ngSkipHydration: 'true' }   ← dernier recours

// ── Erreurs
// NG0500 nœud différent   NG0501 nœuds frères manquants   NG0502 nœud manquant   NG0503 nœuds projetés créés en dehors d’Angular
// NG0504 ngSkipHydration sur un non-composant   NG0505 annotations manquantes (provider absent sur le serveur)
// NG0506 l’application ne devient pas stable en 10 s   NG0507 HTML modifié après le serveur (commentaires supprimés)
// NG0508 triggers hydrate sans hydration incrémentale active

Chap. 12 · 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. SSR, Prerendering et modes de rendu
  2. Hydration : event replay et incrémentale
  3. Code compatible serveur
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