Encyclopédie des erreurs

Nœuds frères manquants lors de l'hydratation

Cette erreur est similaire à l'erreur NG0500, mais elle signifie que le processus d'hydratation d'Angular attendait davantage de nœuds frères à un emplacement précis du DOM et ne les a pas trouvés. Consultez la page de l'erreur NG0500 pour les causes liées à la manipulation directe du DOM.

Une cause fréquente : du contenu conditionnel qui diffère entre le serveur et le client

En dehors de la manipulation directe du DOM, cette erreur survient souvent lorsque le contenu d'un @if ou d'un @for est rendu différemment sur le serveur et lors de l'hydratation côté client. Si le serveur a évalué une condition à true (ou rendu une liste avec plus d'éléments), mais que le client la voit à false (ou avec une liste plus courte) au moment de l'hydratation, le client finit par attendre moins de nœuds frères que le serveur n'en a réellement envoyé. L'hydratation échoue alors en parcourant les nœuds frères.

Un cas fréquent : une valeur provient d'un input(), mais au lieu d'utiliser cet input directement (ou via computed()), elle est copiée une seule fois dans un simple signal() à l'intérieur de ngOnInit() ou du constructeur :

@Component({
  selector: 'app-example',
  template: `
    @if (visibleItems().length) {
      <ul>
        @for (item of visibleItems(); track item) {
          <li>{{ item }}</li>
        }
      </ul>
    }
  `,
})
export class Example {
  items = input<string[]>();

  // This only runs once. If `items()` is not ready yet at the exact
  // moment `ngOnInit` runs, this signal can end up holding a different
  // value on the server than it does on the client. That means a
  // different number of DOM nodes gets rendered on each side, which
  // causes a hydration mismatch.
  visibleItems = signal<string[]>([]);

  ngOnInit() {
    this.visibleItems.set(this.items() ?? []);
  }
}

La solution consiste à dériver la valeur au lieu de la copier une seule fois. Ainsi, chaque rendu, y compris le tout premier, correspond toujours à l'input :

export class Example {
  items = input<string[]>();

  visibleItems = computed(() => this.items() ?? []);
}

Si vous devez aussi permettre aux utilisateurs de remplacer cette valeur localement, par exemple avec un bouton « afficher plus » qui révèle des éléments supplémentaires, utilisez linkedSignal() plutôt qu'un simple signal() défini depuis un lifecycle hook.

Déboguer l'erreur

Consultez l'erreur NG0500 pour des conseils sur le débogage des causes liées à la manipulation directe du DOM.

Cette erreur seule ne vous indique pas quel composant l'a provoquée. L'extension de navigateur Angular DevTools peut vous aider : elle peut mettre en évidence le composant exact où l'incohérence d'hydratation s'est produite. Une fois ce composant trouvé, examinez la façon dont il calcule chaque valeur utilisée dans un @if ou un @for de son template. Vérifiez si cette valeur provient directement d'un input() (via un binding de template ou computed()), ou si elle est copiée dans un signal à l'intérieur d'un lifecycle hook, comme illustré ci-dessus.