Перевод статьи с официального сайта документации Angular
Довольно часто нужно создавать компоненты, которые служат в качестве контейнера для различного типа контета. Например, вы хотите создать камстомный компонент карточку (CustomCard
):
@Component({
selector: 'custom-card',
template: '<div class="card-shadow"> <!-- card content goes here --> </div>',
})
export class CustomCard {/* ... */}
Вы можете использовать <ng-content>
элемент в качестве метки, которая укажет на местоположение вашего будущего контента.
@Component({
selector: 'custom-card',
template: '<div class="card-shadow"> <ng-content></ng-content> </div>',
})
export class CustomCard {/* ... */}
<aside> 💡
<ng-content>
работает аналогично нативному компоненту <slot>
, но с некоторой Angular-специфичной функциональностью.
</aside>
Когда вы используете компонент совместно с <ng-content>
, любой дочерний компонент хост-компонента будет рендериться (отображаться), или по-другому проецироваться, в местоположение <ng-content>
:
// Компонент источник
@Component({
selector: 'custom-card',
template: `
<div class="card-shadow">
<ng-content />
</div>
`,
})
export class CustomCard {/* ... */}
<!-- Использование компонента -->
<custom-card>
<p>This is the projected content</p>
</custom-card>
<!-- отрендеренный DOM -->
<custom-card>
<div class="card-shadow">
<p>This is the projected content</p>
</div>
</custom-card>
Angular относится к любому дочернему компоненту, переданному таким путем, как к содержимому ****компонента. Это отличается от представления компонента, которое относится к элементам, определенным в шаблоне этого компонента.
<ng-content>
элемент не является ни компонентом, ни DOM элементом. Напротив, это специальная метка или указатель, который указывает Angular, где ему необходимо отрендерить контент. Компилятор Angular обрабатывает все элементы <ng-content>
во время сборки. Вы не сможете вставить, удалить или изменить <ng-content>
во время выполнения (в режиме реального времени). Вы не можете добавить директивы, стили или произвольные атрибуты в <ng-content>
.
<ng-content>
не подходит для использования с условиями через @if
, @for
, или @switch
. Angular в любом случае создает экземпляры и DOM - ноды для контента, отрендеренного с помощью метки <ng-content>
, даже в тех ситуациях, когда <ng-content>
скрыт. Для условного рендеринга контента больше подходит ng-template
.
Множественное отображение контента
Angular поддерживает отображение нескольких элементов с помощью нескольких <ng-content>
меток (плейсхолдеров), используя CSS-селекторы. Расширив пример карточки выше, вы можете создать два плейсхолдера для заголовка и тела, используя атрибут select
:
<!-- Шаблон компонента -->
<div class="card-shadow">
<ng-content select="card-title"></ng-content>
<div class="card-divider"></div>
<ng-content select="card-body"></ng-content>
</div>
<!-- Использование компонента -->
<custom-card>
<card-title>Hello</card-title>
<card-body>Welcome to the example</card-body>
</custom-card>
<!-- отрендеренный(сгенерированный) DOM -->
<custom-card>
<div class="card-shadow">
<card-title>Hello</card-title>
<div class="card-divider"></div>
<card-body>Welcome to the example</card-body>
</div>
</custom-card>
<ng-content>
поддерживает те же CSS-селекторы, что и компоненты.
Если вы используете один или более <ng-content>
с select
атрибутом и один <ng-content>
без атрибута, то последний из них (<ng-content>
без атрибута) будет отображать все элементы, которые не совпадают по заданному select
атрибуту:
<!-- Шаблон компонента -->
<div class="card-shadow">
<ng-content select="card-title"></ng-content>
<div class="card-divider"></div>
<!-- отображаем все здесь, исключая "card-title" -->
<ng-content></ng-content>
</div>
<!-- Использование компонента -->
<custom-card>
<card-title>Hello</card-title>
<img src="..." />
<p>Welcome to the example</p>
</custom-card>
<!-- Срендеренный DOM -->
<custom-card>
<div class="card-shadow">
<card-title>Hello</card-title>
<div class="card-divider"></div>
<img src="..." />
<p>Welcome to the example></p>
</div>
</custom-card>
Если компонент не включает в себя <ng-content>
без атрибута select
, то любые элементы, которые не имеют соответствия с одной из меток <ng-content>
компонента с атрибутом, в конечном итоге не будут отрендерены DOM. c
Контент по-умолчанию
Angular обладает возможностью отображать контент по-умолчанию для <ng-content>
компонента на случай, если компонент не имеет никакого подходящего содержимого (дочернего контента). Вы можете определить контент по-умолчанию добавив дочерний контент внутрь элемента <ng-content>
самостоятельно.
<!-- Шаблон компонента -->
<div class="card-shadow">
<ng-content select="card-title">Default Title</ng-content>
<div class="card-divider"></div>
<ng-content select="card-body">Default Body</ng-content>
</div>
<!-- Использование компонента -->
<custom-card>
<card-title>Hello</card-title>
<!-- No card-body provided -->
</custom-card>
<!-- Генерация DOM -->
<custom-card>
<div class="card-shadow">
Hello
<div class="card-divider"></div>
Default Body
</div>
</custom-card>
Именование контента для проецирования
Angular поддерживает специальный атрибут ngProjectAs
, который указать вам на CSS-селектор для любого элемента. Каким бы ни был элемент с указанным атрибутом ngProjectAs
для передачи в <ng-content>
, Angular сравнит значение атрибута select
со значением ngProjectAs
, а не будет пытаться идентифицировать элемент через тег.
<!-- Шаблон компонента -->
<div class="card-shadow">
<ng-content select="card-title"></ng-content>
<div class="card-divider"></div>
<ng-content></ng-content>
</div>
<!-- Использование компонента -->
<custom-card>
<h3 ngProjectAs="card-title">Hello</h3>
<p>Welcome to the example</p>
</custom-card>
<!-- Генерация DOM -->
<custom-card>
<div class="card-shadow">
<h3>Hello</h3>
<div class="card-divider"></div>
<p>Welcome to the example></p>
</div>
</custom-card>
ngProjectAs
поддерживает только статические значения и не может принимать динамические выражения.
Автор: Saiongi