Проекция содержимого¶
В этой теме описывается, как использовать проекцию содержимого для создания гибких, многократно используемых компонентов.
Для просмотра или загрузки кода примера, использованного в этой теме, см. пример.
Проекция содержимого — это шаблон, в котором вы вставляете, или проектируете, содержимое, которое хотите использовать, внутрь другого компонента. Например, у вас может быть компонент Card
, который принимает содержимое, предоставленное другим компонентом.
В следующих разделах описываются общие реализации проекции содержимого в Angular, включая:
Проекция контента | Подробности |
---|---|
Проекция содержимого с одним слотом | При этом типе проекции контента компонент принимает контент из одного источника. |
Проекция содержимого с несколькими слотами | В этом сценарии компонент принимает содержимое из нескольких источников. |
Условная проекция контента | Компоненты, использующие условную проекцию контента, отображают контент только при выполнении определенных условий. |
Однослотовая проекция контента¶
Самой основной формой проекции контента является однослотовая проекция контента. Однослотовая проекция содержимого относится к созданию компонента, в который вы можете спроецировать один компонент.
Чтобы создать компонент, использующий однослотовую проекцию содержимого, выполните следующие действия:
-
В шаблоне компонента добавьте элемент
<ng-content>
в то место, где должно отображаться проецируемое содержимое.
Например, следующий компонент использует элемент <ng-content>
для отображения сообщения.
content-projection/src/app/zippy-basic/zippy-basic.component.ts | |
---|---|
1 2 3 4 5 6 7 8 9 10 |
|
С установленным элементом <ng-content>
пользователи этого компонента теперь могут проецировать свое собственное сообщение в компонент. Например:
content-projection/src/app/app.component.html | |
---|---|
1 2 3 |
|
Элемент <ng-content>
— это заполнитель, который не создает реального элемента DOM. Пользовательские атрибуты, применяемые к <ng-content>
, игнорируются.
Проекция содержимого на несколько слотов¶
Компонент может иметь несколько слотов. Каждый слот может определять CSS-селектор, который определяет, какое содержимое попадает в этот слот.
Этот шаблон называется проекция содержимого на несколько слотов.
При использовании этого шаблона вы должны указать, где должно отображаться проецируемое содержимое.
Для этого используется атрибут select
в <ng-content>
.
Чтобы создать компонент, использующий многослотовую проекцию содержимого, выполните следующие действия:
-
В шаблоне компонента добавьте элемент
<ng-content>
, в котором должно отображаться проецируемое содержимое. -
Добавьте атрибут
select
к элементам<ng-content>
.Angular поддерживает selectors для любой комбинации имени тега, атрибута, CSS-класса и псевдокласса
:not
.Например, следующий компонент использует два элемента
<ng-content>
.content-projection/src/app/zippy-multislot/zippy-multislot.component.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
import { Component } from '@angular/core'; @Component({ selector: 'app-zippy-multislot', template: ` <h2>Multi-slot content projection</h2> Default: <ng-content></ng-content> Question: <ng-content select="[question]"></ng-content> `, }) export class ZippyMultislotComponent {}
Содержимое, использующее атрибут question
, проецируется в элемент <ng-content>
с атрибутом select=[question]
.
content-projection/src/app/app.component.html | |
---|---|
1 2 3 4 |
|
ng-content
без атрибута select
Если ваш компонент включает элемент <ng-content>
без атрибута select
, этот экземпляр получает все спроецированные компоненты, которые не соответствуют ни одному из других элементов <ng-content>
.
В предыдущем примере только второй элемент <ng-content>
определяет атрибут select
. В результате первый элемент <ng-content>
получает любое другое содержимое, проецируемое в компонент.
Условное проецирование содержимого¶
Если ваш компонент должен условно отображать содержимое или отображать его несколько раз, вы должны настроить этот компонент на прием элемента <ng-template>
, который содержит содержимое, которое вы хотите условно отобразить.
Использование элемента <ng-content>
в этих случаях не рекомендуется, потому что когда потребитель компонента предоставляет содержимое, это содержимое всегда инициализируется, даже если компонент не определяет элемент <ng-content>
или если этот элемент <ng-content>
находится внутри оператора ngIf
.
С помощью элемента <ng-template>
вы можете заставить свой компонент явно выводить содержимое на основе любого условия, сколько угодно раз. Angular не будет инициализировать содержимое элемента <ng-template>
до тех пор, пока этот элемент не будет явно отрисован.
Следующие шаги демонстрируют типичную реализацию условного проецирования содержимого с использованием <ng-template>
.
-
В компоненте, который принимает элемент
<ng-template>
, используйте элемент<ng-container>
для визуализации этого шаблона, например:content-projection/src/app/example-zippy.template.html 1 2 3
<ng-container [ngTemplateOutlet]="content.templateRef" ></ng-container>
Этот пример использует директиву
ngTemplateOutlet
для рендеринга заданного элемента<ng-template>
, который вы определите на следующем этапе. Вы можете применить директивуngTemplateOutlet
к любому типу элемента.В данном примере директива назначается элементу
<ng-container>
, поскольку компоненту не нужно отображать реальный элемент DOM. -
Заверните элемент
<ng-container>
в другой элемент, например, в элементdiv
, и примените свою условную логику.1 2 3 4 5
<div *ngIf="expanded" [id]="contentId"> <ng-container [ngTemplateOutlet]="content.templateRef" ></ng-container> </div>
-
В шаблоне, в который вы хотите спроецировать содержимое, оберните спроецированное содержимое в элемент
<ng-template>
, например:1 2 3
<ng-template appExampleZippyContent> It depends on what you do with it. </ng-template>
Элемент
<ng-template>
определяет блок содержимого, который компонент может отобразить на основе своей собственной логики. Компонент может получить ссылку на содержимое шаблона, илиTemplateRef
, используя декораторы@ContentChild
или@ContentChildren
. В предыдущем примере создается пользовательская директиваappExampleZippyContent
в качестве API для обозначения<ng-template>
для содержимого компонента. С помощьюTemplateRef
компонент может выводить содержимое, на которое ссылается, используя либо директивуngTemplateOutlet
, либо методViewContainerRef
createEmbeddedView()
. -
Создайте директиву атрибутов с селектором, который соответствует пользовательскому атрибуту для вашего шаблона. В эту директиву введите экземпляр
TemplateRef
.content-projection/src/app/example-zippy.component.ts 1 2 3 4 5 6 7 8
@Directive({ selector: '[appExampleZippyContent]', }) export class ZippyContentDirective { constructor( public templateRef: TemplateRef<unknown> ) {} }
В предыдущем шаге вы добавили элемент
<ng-template>
с пользовательским атрибутомappExampleZippyContent
. Этот код предоставляет логику, которую Angular будет использовать, когда встретит этот пользовательский атрибут. В данном случае эта логика предписывает Angular создать ссылку на шаблон. -
В компоненте, на который вы хотите спроецировать содержимое, используйте
@ContentChild
для получения шаблона проецируемого содержимого.content-projection/src/app/example-zippy.component.ts 1
@ContentChild(ZippyContentDirective) content!: ZippyContentDirective;
До этого шага в вашем приложении есть компонент, который создает шаблон при выполнении определенных условий. Вы также создали директиву, которая предоставляет ссылку на этот шаблон. На этом последнем шаге декоратор
@ContentChild
инструктирует Angular инстанцировать шаблон в указанном компоненте.В случае многослотовой проекции содержимого используйте
@ContentChildren
, чтобы получитьQueryList
проецируемых элементов.
Проецирование содержимого в более сложных средах¶
Как описано в "Многослотовое проецирование содержимого", вы обычно используете либо атрибут, либо элемент, либо CSS-класс, либо некоторую комбинацию всех трех для определения места проецирования содержимого. Например, в следующем шаблоне HTML тег абзаца использует пользовательский атрибут question
для проецирования содержимого в компонент app-zippy-multislot
.
content-projection/src/app/app.component.html | |
---|---|
1 2 3 4 |
|
В некоторых случаях вы можете захотеть спроецировать содержимое как другой элемент. Например, содержимое, которое вы хотите спроецировать, может быть дочерним элементом другого элемента.
Для этого используется атрибут ngProjectAs
.
Например, рассмотрим следующий фрагмент HTML:
content-projection/src/app/app.component.html | |
---|---|
1 2 3 |
|
В этом примере используется атрибут <ng-container>
для имитации проецирования компонента в более сложную структуру.
Напоминание
Элемент ng-container
— это логическая конструкция, которая используется для группировки других элементов DOM; однако сам ng-container
не отображается в дереве DOM.
В данном примере содержимое, которое мы хотим спроецировать, находится внутри другого элемента. Чтобы спроецировать это содержимое, шаблон использует атрибут ngProjectAs
. С помощью ngProjectAs
весь элемент <ng-container>
проецируется в компонент с помощью селектора [question]
.
28.02.2022