Как условно обернуть ‹ng-content› несколько раз?

Я попытался написать компонент-оболочку, которому можно передать некоторый текст и некоторые параметры, которые затем будут форматировать мой текст с помощью начальной загрузки на основе этих параметров.

Я пробовал это:

header-line.component.ts

@Component({
  selector:'header-line',
  templateUrl: 'header-line.component.html'
})
export class HeaderLineComponent {
  @Input level: number;
  // other inputs used for formatting skipped here
}

header-line.component.html

<div class="row">
  <div class="col-sm-12"> <!-- would really be [className]="..." based on skipped inputs -->
    <header-wrapper [level]="level">
      <!-- display of icon, based on skipped inputs -->
      <ng-content></ng-content>
    </header-wrapper>
  </div>
</div>

header-wrapper.component.ts

@Component({
  selector: 'header-wrapper',
  templateUrl: './header-wrapper.component.html'
})
export class HeaderWrapperComponent {
  @Input() level: number;
}

header-wrapper.component.html

<h1 *ngIf="level === 1"><ng-content></ng-content></h1>
<h2 *ngIf="level === 2"><ng-content></ng-content></h2>
<h3 *ngIf="level === 3"><ng-content></ng-content></h3>
<h4 *ngIf="level === 4"><ng-content></ng-content></h4>
<h5 *ngIf="level === 5"><ng-content></ng-content></h5>
<span *ngIf="!level || level < 1 || level > 5"><ng-content></ng-content></span>

Предполагаемое использование

Использование будет примерно таким:

<header-line [level]="1" [...]="...">Just a test h1</header-line>
<header-line [level]="2" [...]="...">Just a test h2</header-line>
<header-line [level]="3" [...]="...">Just a test h3</header-line>
<header-line [...]="...">Just a test span</header-line>

Ожидаемый результат

Тогда я ожидал бы, что вывод создаст что-то эквивалентное:

<div class="row">
  <div class="cols-sm-12">
    <h1>Just a test h1</h1>
  </div>
</div>
<div class="row">
  <div class="cols-sm-12">
    <h2>Just a test h2</h2>
  </div>
</div>
<div class="row">
  <div class="cols-sm-12">
    <h3>Just a test h3</h3>
  </div>
</div>
<div class="row">
  <div class="cols-sm-12">
    <span>Just a test span</span>
  </div>
</div>

Эффективный результат

Однако я получаю следующее:

<div class="row">
  <div class="cols-sm-12">
  </div>
</div>
<div class="row">
  <div class="cols-sm-12">
  </div>
</div>
<div class="row">
  <div class="cols-sm-12">
  </div>
</div>
<div class="row">
  <div class="cols-sm-12">
    <span>Just a test span</span>
  </div>
</div>

Анализ проблем

Мне потребовалось совсем немного времени, чтобы выяснить, что причиной моей проблемы является повторное использование в header-wrapper.component.ts, потому что он, по-видимому, статичен и не может использоваться динамически.

Следующие две ссылки объясняют, почему мои ожидания не оправдались:

https://github.com/angular/angular/issues/9173

https://github.com/angular/angular/issues/8563

Поиск решения

Следующая ссылка на Stackoverflow показывает, как это можно сделать, если необходимо поддерживать только два случая:

Как условно обернуть div вокруг ng-контента

Используя этот подход, мне удалось следующее:

Обновлено: header-wrapper.component.html

<h1 *ngIf="level && level === 1; else notOne">
  <ng-container *ngTemplateOutlet="content"></ng-container>
</h1>

<ng-template #notOne>
  <h2 *ngIf="level && level === 2; else notTwo">
    <ng-container *ngTemplateOutlet="content"></ng-container>
  </h2>
</ng-template>

<ng-template #notTwo>
  <h3 *ngIf="level && level === 3; else notThree">
    <ng-container *ngTemplateOutlet="content"></ng-container>
  </h3>
</ng-template>

<ng-template #notThree>
  <h4 *ngIf="level && level === 4; else notFour">
    <ng-container *ngTemplateOutlet="content"></ng-container>
  </h4>
</ng-template>

<ng-template #notFour>
  <h5 *ngIf="level && level === 5; else content">
    <ng-container *ngTemplateOutlet="content"></ng-container>
  </h5>
</ng-template>

<ng-template #content>
  <ng-content></ng-content>
</ng-template>

Это дает желаемый результат.

Мой вопрос

Это действительно единственный способ сделать это? Или есть более простой способ, который мне не хватает?


person Urs Beeli    schedule 31.01.2019    source источник
comment
Обновленный подход — это правильный способ встраивания включенного контента в несколько мест. Больше ничего. Вы можете посмотреть youtube.com/watch?v=PTwKhxLZ3jI.   -  person Jota.Toledo    schedule 31.01.2019


Ответы (1)


Основываясь на видео, на которое ссылается Jota.Toledo, я придумал более компактную версию.

Обновлен header-wrapper.component.html

<h1 *ngIf="level && level === 1"><ng-container [ngTemplateOutlet]="content"></ng-container></h1>
<h2 *ngIf="level && level === 2"><ng-container [ngTemplateOutlet]="content"></ng-container></h2>
<h3 *ngIf="level && level === 3"><ng-container [ngTemplateOutlet]="content"></ng-container></h3>
<h4 *ngIf="level && level === 4"><ng-container [ngTemplateOutlet]="content"></ng-container></h4>
<h5 *ngIf="level && level === 5"><ng-container [ngTemplateOutlet]="content"></ng-container></h5>
<span *ngIf="!level || level < 1 || level > 5"><ng-container [ngTemplateOutlet]="content"></ng-container></span>

Это работает так же, как мое обновленное решение из вопроса, но я считаю эту версию более компактной и более читаемой.

person Urs Beeli    schedule 31.01.2019