Структурные директивы¶
30.09.2026
Структурные директивы применяют к элементу <ng-template>, и они по условию или повторно отрисовывают содержимое этого <ng-template>.
Для обычной условной и повторной отрисовки берите встроенные блоки управления потоком (@if, @for и @switch). Структурную директиву пишите, когда нужно переиспользуемое поведение отрисовки, которого в управлении потоком нет: например, показывать содержимое только после проверки прав или отдавать шаблону данные из внешнего источника.
Пример¶
Сквозной пример этого руководства — директива SelectDirective. Она забирает данные из заданного источника и отрисовывает свой шаблон, когда данные уже есть. Имя взято от ключевого слова SQL SELECT, селектор атрибута — [select].
У SelectDirective есть вход selectFrom: он называет источник данных. Префикс select у этого входа важен для сокращённого синтаксиса. Директива создаёт экземпляр своего <ng-template> с контекстом шаблона, в котором лежат выбранные данные.
Напрямую на <ng-template> это выглядит так:
1 2 3 | |
Структурная директива может дождаться, пока данные появятся, и только потом отрисовать свой <ng-template>.
Элемент <ng-template> в Angular задаёт шаблон, который по умолчанию ничего не рисует. Если обернуть элементы в <ng-template> и не применить структурную директиву, эти элементы не отрисуются.
Подробнее — в документации API ng-template.
Сокращённый синтаксис структурных директив¶
У структурных директив есть сокращённый синтаксис: элемент <ng-template> писать явно не нужно.
Директиву можно повесить прямо на элемент, поставив звёздочку (*) перед селектором атрибута, например *select. Angular превращает звёздочку перед структурной директивой в <ng-template>, который принимает директиву и оборачивает элемент вместе с потомками.
С SelectDirective это выглядит так:
1 | |
На примере видна гибкость сокращённого синтаксиса структурных директив. Его иногда называют микросинтаксисом.
В таком виде на <ng-template> попадают только структурная директива и её привязки. Остальные атрибуты и привязки тега <p> остаются на месте. Например, две записи ниже равносильны:
1 2 3 4 5 6 7 | |
Сокращение раскрывается по набору соглашений. Ниже задана более полная грамматика, а превращение в примере выше устроено так.
Первая часть выражения *select — let data: объявляется переменная шаблона data. Присваивания после неё нет, поэтому переменная привязывается к свойству контекста шаблона $implicit.
Вторая часть — пара «ключ и выражение», from source. from — ключ привязки, source — обычное выражение шаблона. Ключи привязки сопоставляются со свойствами так: ключ переводят в PascalCase и спереди добавляют селектор структурной директивы. Ключ from становится selectFrom и привязывается к выражению source. Поэтому у многих структурных директив имена входов начинаются с селектора самой директивы.
Одна структурная директива на элемент¶
В сокращённом синтаксисе на элемент можно повесить только одну структурную директиву: раскрывается она в один элемент <ng-template>. Несколько директив потребовали бы нескольких вложенных <ng-template>, и неясно, какая должна быть первой. <ng-container> даёт слои-обёртки, когда несколько структурных директив нужно повесить вокруг одного и того же физического элемента DOM или компонента: вложенность задаёте вы сами.
Создание структурной директивы¶
Структурная директива — класс директивы, который внедряет две зависимости:
TemplateRefдаёт директиве доступ к содержимому того<ng-template>, на который она повешена.ViewContainerRef— место в DOM, где директива может отрисовать этот шаблон.
Отрисовкой директива управляет сама: создаёт или не создаёт встроенные представления из шаблона в контейнере представлений. Полный SelectDirective выглядит так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | |
Вход selectFrom называет источник, из которого директива читает данные. Здесь стоит input.required(): без источника директиве нечего делать.
Когда Angular инициализирует директиву, она загружает данные и отрисовывает шаблон вызовом createEmbeddedView(). Второй аргумент — объект контекста шаблона: значения, к которым шаблон привязывается объявлениями let. Данные в ключе $implicit становятся значением по умолчанию, которое получает let-data (или let data в сокращении).
В этом примере шаблон отрисовывается один раз, при инициализации директивы. Когда привязанный источник данных меняется, повторной отрисовки нет.
Когда директива уже работает, имеет смысл добавить поддержку проверки типов шаблона.
Команда CLI ng generate directive создаёт заготовку директивы и файл её теста.
Справочник синтаксиса структурных директив¶
Для своих структурных директив используйте такой синтаксис:
1 | |
Каждую часть грамматики задают такие шаблоны:
1 2 3 | |
| Ключевое слово | Подробности |
|---|---|
prefix | Ключ HTML-атрибута |
key | Ключ HTML-атрибута |
local | Имя локальной переменной в шаблоне |
export | Значение, которое директива экспортирует под заданным именем |
expression | Обычное выражение Angular |
Как Angular раскрывает сокращение¶
Angular переводит сокращение структурной директивы в обычный синтаксис привязок так:
| Сокращение | Перевод |
|---|---|
prefix и голое expression | [prefix]="expression" |
keyExp | [prefixKey]="expression" (prefix добавляется к key) |
let local | let-local="export" |
Примеры сокращений¶
В таблице — примеры сокращений:
| Сокращение | Как Angular читает синтаксис |
|---|---|
*myDir="let item of [1,2,3]" | <ng-template myDir let-item [myDirOf]="[1, 2, 3]"> |
*myDir="let item of [1,2,3] as items; trackBy: myTrack; index as i" | <ng-template myDir let-item [myDirOf]="[1,2,3]" let-items="myDirOf" [myDirTrackBy]="myTrack" let-i="index"> |
*ngComponentOutlet="componentClass" | <ng-template [ngComponentOutlet]="componentClass"> |
*ngComponentOutlet="componentClass; inputs: myInputs" | <ng-template [ngComponentOutlet]="componentClass" [ngComponentOutletInputs]="myInputs"> |
*myDir="exp as value" | <ng-template [myDir]="exp" let-value="myDir"> |
Проверка типов шаблона для своих директив¶
Проверку типов шаблона для своих директив усиливают охранники шаблона в определении директивы. Они помогают проверке типов шаблона Angular находить ошибки ещё при компиляции и тем самым избегать ошибок во время выполнения. Охранники бывают двух видов:
ngTemplateGuard_(input)задаёт, как сужать выражение входа по типу конкретного входа.ngTemplateContextGuardпо типу самой директивы определяет тип объекта контекста шаблона.
Ниже — примеры обоих видов. Подробнее — в разделе Проверка типов шаблона.
Сужение типа охранниками шаблона¶
Структурная директива в шаблоне решает, отрисовывать ли этот шаблон во время выполнения. Некоторым директивам нужно сузить тип по типу входного выражения.
Входные охранники дают два сужения:
- Сужение входного выражения функцией утверждения типа TypeScript.
- Сужение входного выражения по его истинности.
Чтобы сузить входное выражение, объявите функцию утверждения типа:
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
В шаблоне проверка типов идёт так, будто для выражения, привязанного ко входу, уже сработал ngTemplateGuard_actor.
Некоторые директивы рисуют шаблон только при истинном входе. Семантику истинности целиком в функции утверждения типа не передать, поэтому вместо неё указывают литеральный тип 'binding': он говорит проверке типов шаблона, что охранником должно быть само выражение привязки.
1 2 3 4 5 6 | |
Проверка типов шаблона считает, что выражение, привязанное к condition, внутри шаблона истинно.
Типизация контекста директивы¶
Если структурная директива отдаёт контекст созданному шаблону, тип этого контекста внутри шаблона задаёт статическая функция утверждения типа ngTemplateContextGuard. Она выводит тип контекста из типа директивы. Это нужно, когда директива обобщённая.
Для SelectDirective выше можно описать ngTemplateContextGuard и верно задать тип данных, даже если источник данных обобщённый.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | |
Что дальше¶
Источник: https://angular.dev/guide/directives/structural-directives