Skip to content

ng-template и ngTemplateOutlet

1. ng-template

<ng-template> — это элемент Angular, который определяет шаблон, но не рендерится сам по себе. Он используется как «заготовка», которую можно вставить в нужное место.

html
<!-- Этот шаблон НЕ отобразится автоматически -->
<ng-template>
  <p>Этот текст не виден по умолчанию</p>
</ng-template>

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

html
<!-- *ngIf под капотом использует ng-template -->
<div *ngIf="isVisible">Видимый контент</div>

<!-- Эквивалентная развёрнутая запись -->
<ng-template [ngIf]="isVisible">
  <div>Видимый контент</div>
</ng-template>

ng-template с else

html
<div *ngIf="user; else noUser">
  <p>Привет, {{ user.name }}!</p>
</div>

<ng-template #noUser>
  <p>Пожалуйста, войдите в систему</p>
</ng-template>

ng-template с then/else

html
<div *ngIf="isLoggedIn; then loggedIn; else loggedOut"></div>

<ng-template #loggedIn>
  <p>Добро пожаловать!</p>
</ng-template>

<ng-template #loggedOut>
  <p>Войдите в систему</p>
</ng-template>

2. ng-container

<ng-container> — логический контейнер, который не создаёт DOM-элемент. Полезен когда нужно применить директиву без добавления лишнего HTML.

html
<!-- Без ng-container — лишний div -->
<div *ngIf="isVisible">
  <span *ngFor="let item of items">{{ item }}</span>
</div>

<!-- С ng-container — чистый DOM -->
<ng-container *ngIf="isVisible">
  <span *ngFor="let item of items">{{ item }}</span>
</ng-container>

Комбинирование директив

Angular не позволяет две структурные директивы на одном элементе. ng-container решает эту проблему:

html
<!-- ОШИБКА: нельзя *ngIf и *ngFor на одном элементе -->
<!-- <li *ngIf="show" *ngFor="let item of items">{{ item }}</li> -->

<!-- Правильно -->
<ng-container *ngIf="show">
  <li *ngFor="let item of items">{{ item }}</li>
</ng-container>

3. ngTemplateOutlet

*ngTemplateOutlet позволяет вставить ng-template в указанное место и передать ему контекст.

Базовое использование

html
<ng-template #greetTemplate>
  <p>Привет из шаблона!</p>
</ng-template>

<!-- Вставляем шаблон здесь -->
<ng-container *ngTemplateOutlet="greetTemplate"></ng-container>

<!-- Можно вставить несколько раз -->
<ng-container *ngTemplateOutlet="greetTemplate"></ng-container>

Передача контекста

html
<ng-template #userCard let-name="name" let-role="role">
  <div class="card">
    <h3>{{ name }}</h3>
    <p>Роль: {{ role }}</p>
  </div>
</ng-template>

<ng-container *ngTemplateOutlet="userCard; context: { name: 'Иван', role: 'Админ' }">
</ng-container>

<ng-container *ngTemplateOutlet="userCard; context: { name: 'Мария', role: 'Разработчик' }">
</ng-container>

Переменная $implicit

$implicit — значение по умолчанию, доступное через let-переменная без указания ключа:

html
<ng-template #itemTemplate let-item>
  <li>{{ item }}</li>
</ng-template>

<ul>
  <ng-container *ngFor="let fruit of fruits">
    <ng-container *ngTemplateOutlet="itemTemplate; context: { $implicit: fruit }">
    </ng-container>
  </ng-container>
</ul>
typescript
@Component({ /* ... */ })
export class FruitListComponent {
  fruits = ['Яблоко', 'Банан', 'Вишня'];
}

4. Динамический выбор шаблона

typescript
@Component({
  selector: 'app-dynamic',
  template: `
    <ng-template #adminTemplate>
      <div class="admin-panel">
        <h2>Панель администратора</h2>
        <button>Управление пользователями</button>
      </div>
    </ng-template>

    <ng-template #userTemplate>
      <div class="user-panel">
        <h2>Личный кабинет</h2>
        <p>Добро пожаловать!</p>
      </div>
    </ng-template>

    <ng-container *ngTemplateOutlet="isAdmin ? adminTemplate : userTemplate">
    </ng-container>
  `
})
export class DynamicComponent {
  isAdmin = false;
}

5. Передача шаблона в дочерний компонент

Родительский компонент

html
<app-list [items]="products" [itemTemplate]="productTpl">
  <ng-template #productTpl let-product>
    <div class="product">
      <strong>{{ product.name }}</strong>
      <span>{{ product.price }} руб.</span>
    </div>
  </ng-template>
</app-list>

Дочерний компонент

typescript
@Component({
  selector: 'app-list',
  template: `
    <div *ngFor="let item of items">
      <ng-container
        *ngTemplateOutlet="itemTemplate; context: { $implicit: item }">
      </ng-container>
    </div>
  `
})
export class ListComponent {
  @Input() items: any[] = [];
  @Input() itemTemplate!: TemplateRef<any>;
}

Сводная таблица

ЭлементНазначениеСоздаёт DOM?
ng-templateОпределяет шаблон-заготовкуНет
ng-containerЛогический контейнер для директивНет
*ngTemplateOutletВставляет ng-template в DOMНет (вставляет содержимое)
let-varПолучает переменную из контекста
$implicitЗначение по умолчанию в контексте