Encyclopédie des erreurs

Control flow empêchant la projection de contenu

Un élément ou un <ng-template> placé dans un bloc de control flow devrait être projeté dans un slot <ng-content> précis, mais ne l'est pas, car le bloc a plus d'un nœud racine.

import {Component} from '@angular/core';

@Component({
  selector: 'app-card',
  template: `
    <ng-content select="[card-title]" />
    <ng-content />
  `,
})
class Card {}

@Component({
  imports: [Card],
  // NG8011: <h2 card-title> is not projected into the "[card-title]" slot because the @if
  // block has two root nodes (the h2 and the p).
  template: `
    <app-card>
      @if (showTitle) {
        <h2 card-title>Title</h2>
        <p>Subtitle</p>
      }
    </app-card>
  `,
})
class MyComponent {
  showTitle = true;
}

Déboguer l'erreur

Les blocs de control flow émulent le comportement de projection de contenu de *ngIf et *ngFor, qui projettent l'élément sur lequel ils sont appliqués. Cela ne fonctionne que si le bloc a un seul nœud racine. Lorsque le bloc a plus d'un nœud racine, Angular ne peut pas utiliser l'élément pour choisir un slot : <h2 card-title> se retrouve donc dans le slot <ng-content /> par défaut de Card au lieu du slot [card-title].

Le texte compte comme un nœud racine : une ligne de texte isolée à côté de l'élément projeté provoque donc le même problème, sauf si le composant qui contient le bloc définit preserveWhitespaces: true, auquel cas les espaces blancs comptent aussi.

Pour résoudre l'erreur, choisissez l'une des approches suivantes :

  • Enveloppez le contenu dans un <ng-container> qui utilise ngProjectAs pour correspondre au selector du slot. Tout ce qui se trouve dans le conteneur est projeté dans ce slot.

    @Component({
      imports: [Card],
      template: `
        <app-card>
          @if (showTitle) {
            <ng-container ngProjectAs="[card-title]">
              <h2>Title</h2>
              <p>Subtitle</p>
            </ng-container>
          }
        </app-card>
      `,
    })
    class MyComponent {
      showTitle = true;
    }
  • Répartissez le contenu sur plusieurs blocs, de sorte que chacun ait un seul nœud projetable à sa racine.

    @Component({
      imports: [Card],
      template: `
        <app-card>
          @if (showTitle) {
            <h2 card-title>Title</h2>
          }
          @if (showTitle) {
            <p>Subtitle</p>
          }
        </app-card>
      `,
    })
    class MyComponent {
      showTitle = true;
    }