Error Encyclopedia

Control flow preventing content projection

An element or <ng-template> inside a control flow block would be projected into a specific <ng-content> slot, but isn't because the block has more than one root node.

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;
}

Debugging the error

Control flow blocks emulate the content projection behavior of *ngIf and *ngFor, which project the element they are applied to. This only works if the block has a single root node. When the block has more than one root node, Angular can't use the element to choose a slot, so <h2 card-title> ends up in the default <ng-content /> slot of Card instead of the [card-title] slot.

Text counts as a root node, so a stray line of text next to the projected element causes the same problem, unless the component that contains the block sets preserveWhitespaces: true, in which case whitespace counts as well.

To resolve the error, pick one of the following approaches:

  • Wrap the content in an <ng-container> that uses ngProjectAs to match the selector of the slot. Everything inside the container is projected into that 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;
    }
  • Split the content across multiple blocks, so that each one has a single projectable node at its root.

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