В чём разница между ng-content, ng-container и ng-template?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Разница между ng-content, ng-container и ng-template в Angular
Эти три конструкции в Angular служат разным целям и часто используются в шаблонах. Понимание их различий критично для написания переиспользуемых и гибких компонентов.
ng-content
ng-content используется для создания слотов контента в компонентах (Content Projection). Это позволяет родительскому компоненту передавать контент в дочерний.
// child.component.ts
import { Component } from @angular/core;
@Component({
selector: app-card,
template: `
<div class="card">
<div class="card-header">
<ng-content select=".header"></ng-content>
</div>
<div class="card-body">
<ng-content></ng-content>
</div>
<div class="card-footer">
<ng-content select=".footer"></ng-content>
</div>
</div>
`
})
export class CardComponent {}
<!-- parent.component.html -->
<app-card>
<div class="header">Заголовок карточки</div>
<p>Основное содержимое</p>
<div class="footer">Подвал карточки</div>
</app-card>
Характеристики ng-content:
- Служит для projection (передачи содержимого)
- Сам не отрисовывается (заменяется содержимым)
- Может использовать select для выбора конкретного контента
- Создает один элемент в DOM
ng-container
ng-container — это логический контейнер, который не создает элемент в DOM. Используется для группировки элементов без добавления дополнительного DOM узла.
// component.ts
import { Component } from @angular/core;
@Component({
selector: app-user-list,
template: `
<div>
<ng-container *ngIf="users.length > 0">
<p>Количество пользователей: {{ users.length }}</p>
<ul>
<li *ngFor="let user of users">{{ user.name }}</li>
</ul>
</ng-container>
<ng-container *ngIf="users.length === 0">
<p>Пользователи не найдены</p>
</ng-container>
</div>
`
})
export class UserListComponent {
users = [];
}
Характеристики ng-container:
- Логический контейнер
- Не создает DOM элемента
- Полезен для условного отображения (ngIf)
- Полезен для циклов (ngFor)
- Полезен для ng-switch
- Может применять директивы
ng-template
ng-template — это шаблон, который не отрисовывается по умолчанию. Используется с директивами (*ngIf, *ngFor, ngTemplateOutlet) для определения повторно используемого шаблона.
// component.ts
import { Component, TemplateRef, ViewChild } from @angular/core;
@Component({
selector: app-template-demo,
template: `
<ng-template #greeting let-name="name">
<p>Привет, {{ name }}!</p>
</ng-template>
<div *ngTemplateOutlet="greeting; context: { name: Иван }"></div>
<div *ngTemplateOutlet="greeting; context: { name: Мария }"></div>
`
})
export class TemplateDemoComponent {
@ViewChild(greeting) greetingTemplate: TemplateRef<any>;
}
Характеристики ng-template:
- Определяет шаблон, который не отрисовывается по умолчанию
- Используется с ngTemplateOutlet для отрисовки
- Поддерживает контекст (передачу переменных)
- Может быть переиспользован несколько раз
- Используется с директивами (*ngIf, *ngSwitch)
Практическое сравнение
Пример 1: Условное отображение
<!-- Плохо - добавляет лишний div -->
<div *ngIf="isVisible">
<p>Видимо</p>
<span>Текст</span>
</div>
<!-- Хорошо - не добавляет DOM элемент -->
<ng-container *ngIf="isVisible">
<p>Видимо</p>
<span>Текст</span>
</ng-container>
Пример 2: Циклы
<!-- Плохо - добавляет лишний div -->
<div *ngFor="let item of items">
<span>{{ item }}</span>
</div>
<!-- Хорошо - без лишних элементов -->
<ng-container *ngFor="let item of items">
<span>{{ item }}</span>
</ng-container>
Пример 3: Переиспользуемые компоненты
// list.component.ts
import { Component, Input, TemplateRef } from @angular/core;
@Component({
selector: app-generic-list,
template: `
<div class="list">
<div *ngFor="let item of items" class="list-item">
<ng-container *ngTemplateOutlet="itemTemplate; context: { $implicit: item }"></ng-container>
</div>
</div>
`
})
export class GenericListComponent {
@Input() items: any[] = [];
@Input() itemTemplate: TemplateRef<any>;
}
<!-- parent.html -->
<app-generic-list [items]="users" [itemTemplate]="userTemplate"></app-generic-list>
<ng-template #userTemplate let-user>
<p>{{ user.name }} - {{ user.email }}</p>
</ng-template>
Пример 4: ng-switch с шаблонами
<ng-container [ngSwitch]="status">
<ng-template ngSwitchCase="loading">
<p>Загрузка...</p>
</ng-template>
<ng-template ngSwitchCase="success">
<p>Успешно загружено</p>
</ng-template>
<ng-template ngSwitchCase="error">
<p>Произошла ошибка</p>
</ng-template>
<ng-template ngSwitchDefault>
<p>Неизвестное состояние</p>
</ng-template>
</ng-container>
Таблица сравнения
| Характеристика | ng-content | ng-container | ng-template |
|---|---|---|---|
| Создает DOM элемент | Нет | Нет | Нет |
| Для projection | Да | Нет | Нет |
| Для условных блоков | Нет | Да | Да |
| Для циклов | Нет | Да | Да |
| Переиспользуемый | Нет | Нет | Да |
| С контекстом | Нет | Нет | Да |
| С ngTemplateOutlet | Нет | Нет | Да |
Практические примеры использования
1. Модальное окно с projection
@Component({
selector: app-modal,
template: `
<div class="modal" *ngIf="isOpen">
<div class="modal-header">
<ng-content select=".header"></ng-content>
</div>
<div class="modal-body">
<ng-content></ng-content>
</div>
<div class="modal-footer">
<ng-content select=".footer"></ng-content>
</div>
</div>
`
})
export class ModalComponent {
@Input() isOpen = false;
}
<app-modal [isOpen]="showModal">
<div class="header">Подтверждение</div>
<p>Вы уверены?</p>
<div class="footer">
<button (click)="confirm()">Да</button>
<button (click)="cancel()">Нет</button>
</div>
</app-modal>
2. Таблица с кастомными ячейками
@Component({
selector: app-data-table,
template: `
<table>
<tr *ngFor="let row of rows">
<td *ngFor="let column of columns">
<ng-container *ngTemplateOutlet="getCellTemplate(column); context: { $implicit: row[column.field] }"></ng-container>
</td>
</tr>
</table>
`
})
export class DataTableComponent {
@Input() rows: any[] = [];
@Input() columns: any[] = [];
@Input() cellTemplates: { [key: string]: TemplateRef<any> } = {};
getCellTemplate(column: any) {
return this.cellTemplates[column.field] || this.defaultCellTemplate;
}
@ViewChild(defaultCell) defaultCellTemplate: TemplateRef<any>;
}
3. Условный рендеринг с минимальным DOM
<!-- ng-container не добавляет элемент в DOM -->
<section>
<h2>Список пользователей</h2>
<ng-container *ngIf="users.length > 0; else noUsers">
<ul>
<li *ngFor="let user of users">{{ user.name }}</li>
</ul>
</ng-container>
<ng-template #noUsers>
<p>Пользователи не найдены</p>
</ng-template>
</section>
Рекомендации
- ng-content - для создания переиспользуемых компонентов с гибким контентом
- ng-container - для группировки элементов без добавления DOM узла (условия, циклы)
- ng-template - для определения переиспользуемых шаблонов с контекстом
Вывод: Правильное использование этих конструкций делает Angular код чище, производительнее и более переиспользуемым. ng-container часто недооценивается, но это мощный инструмент для минимизации DOM иерархии.