В этом руководстве рассматривается, как Angular управляет DOM с помощью структурных директив и как вы можете написать свои собственные структурные директивы, чтобы делать то же самое.
Что такое структурные директивы ?
Структурные директивы отвечают за верстку HTML. Они формируют и изменяют структуру DOM, обычно путем добавления, удаления или изменения элементов.
Как и в случае с другими директивами, вы применяете структурную директиву к элементу хоста. Затем директива делает все, что она должна делать с этим элементом хоста и его потомками.
Структурные директивы легко распознать. Звездочка (*) предшествует имени атрибута директивы, как в этом примере. (src/app/app.component.html)
{{hero.name}}
Live Demo:
Это просто фрагмент кода, объясняющий конкретную концепцию, и может не иметь вывода
См. Структурные директивы пера от w3resource (@ w3resource) на CodePen.
Следует отметить, что никаких скобок, никаких скобок только * ngIf устанавливается в строку.
В этом руководстве вы узнаете, что звездочка (*) — это удобная нотация, а строка — это микросинтаксис, а не обычное выражение шаблона. Angular обессахаривает эту нотацию в размеченный , который окружает основной элемент и его потомки. Каждая структурная директива делает что-то свое с этим шаблоном.
Существует три наиболее распространенных встроенных структурных директивы, которые включают
- NgIf
- NgFor
- NgSwitch
Вот их пример в шаблоне: (src/app/app.component.html )
{{hero.name}} - {{hero.name}}
Live Demo:
Это просто фрагмент кода, объясняющий конкретную концепцию, и может не иметь никаких output
См. Примеры пера от w3resource (@ w3resource) на CodePen.
В этом последнем разделе этого является учебным пособием, мы рассмотрим, как они работают и как написать нашу собственную структурную директиву.
Directive Spelling
На протяжении всего этого руководства , вы увидите директиву, написанную как в UpperCamelCase, так и в lowerCamelCase. Вы уже видели NgIf и ngIf. Есть причина. NgIf относится к классу директивы; ngIf относится к имени атрибута директивы.
Класс директивы записывается в UpperCamelCase (NgIf). Имя атрибута директивы пишется в lowerCamelCase (ngIf). В руководстве говорится о классе директивы, когда речь идет о его свойствах и о том, что делает директива. Руководство ссылается на имя атрибута при описании того, как вы применяете директиву к элементу в шаблоне HTML.
Есть два других вида директив Angular: (1) компоненты и (2) директивы атрибутов .
Директива компонента управляет областью HTML так же, как собственный элемент HTML. Технически это директива с шаблоном.
Директива атрибута изменяет внешний вид или поведение элемента, компонента или другой директивы. Например, встроенная директива NgStyle изменяет несколько стилей элементов одновременно.
Вы можете применить несколько директив атрибутов к одному элементу хоста, но вы можете применить только одну структурную директиву к элементу хоста .
Пример использования NgIf
NgIf — простейшая структурная директива, которую легче всего понять. Он принимает логическое выражение и заставляет весь фрагмент модели DOM появляться или исчезать.
Выражение истинно, а ngIf истинно. Этот абзац находится в DOM.
Выражение ложно, а ngIf - ложно. Этого абзаца нет в DOM.
Живая демонстрация:
Это просто Фрагмент кода, объясняющий конкретную концепцию и не имеющий вывода
См. Pen Ng-if by w3resource (@ w3resource) на CodePen.
Директива ngIf не скрывает элементы с помощью CSS. Он добавляет и удаляет их физически из DOM. Подтвердите этот факт с помощью инструментов разработчика браузера для проверки DOM.
верхний абзац находится в DOM. Нижнего, вышедшего из употребления абзаца нет; на его месте находится комментарий о «привязках».
Когда условие ложно, NgIf удаляет свой хост-элемент из DOM, отсоединяет его от событий DOM (вложений, которые он сделал), отсоединяет компонент из обнаружения изменений Angular и уничтожает его. Компоненты и узлы DOM могут быть собраны мусором и освободить память.
Почему remove , а не hide ?
Вместо этого директива может скрыть нежелательный абзац, установив для его стиля отображения значение none.
Выражение устанавливает отображение на "block". Этот абзац виден.
Выражение устанавливает для display значение "none". Этот абзац скрыт, но все еще находится в DOM.
Живая демонстрация:
Это просто фрагмент кода, объясняющий конкретную концепцию, и может нет вывода
См. Pen display-none от w3resource (@ w3resource) на CodePen.
Хотя невидимый элемент остается в DOM
Разница между скрытием и удалением не имеет значения для простой абзац. Имеет значение, когда хост-элемент присоединен к ресурсоемкому компоненту. Такое поведение компонента продолжается, даже когда он скрыт. Компонент остается прикрепленным к своему элементу DOM. Он продолжает прислушиваться к событиям. Angular продолжает проверять изменения, которые могут повлиять на привязки данных. Что бы ни делал компонент, он продолжает делать.
Несмотря на то, что этот компонент и все его дочерние компоненты не видны, они связывают ресурсы. Производительность и нагрузка на память могут быть значительными, отзывчивость может ухудшиться, и пользователь ничего не видит.
С положительной стороны, отображение элемента снова происходит быстро. Предыдущее состояние компонента сохраняется и готово к отображению. Компонент не выполняет повторную инициализацию — операция, которая может быть дорогостоящей. Итак, скрытие и отображение иногда является правильным занятием.
Но в отсутствие веских причин для их сохранения вы должны отдать предпочтение удалению элементов DOM, которые пользователь не может видеть. и восстановить неиспользуемые ресурсы с помощью структурной директивы, такой как NgIf.
Эти же соображения применимы ко всем структурным директивам, встроенным или настраиваемым. Перед применением структурной директивы вы можете ненадолго остановиться, чтобы рассмотреть последствия добавления и удаления элементов, а также создания и уничтожения компонентов.
Префикс звездочки (*)
Наверняка вы обратили внимание на префикс звездочки (*) в названии директивы и задались вопросом, зачем она нужна и для чего она нужна.
Вот * ngIf отображение имени героя, если герой существует.
{{hero.name}}
Живая демонстрация:
Это просто фрагмент кода, объясняющий конкретную концепцию и не имеющий вывода
См. Pen xNZExL от w3resource (@ w3resource) на CodePen.
Звездочка — это «синтаксический сахар» для чего-то более сложного. Внутри Angular преобразует атрибут * ngIf в элемент , обернутый вокруг основного элемента, вот так.
{{hero.name}}
Живая демонстрация:
Это просто фрагмент кода, объясняющий конкретную концепцию, и может не иметь вывода
См. Pen ng-if от w3resource (@ w3resource) на CodePen .
- Директива * ngIf перемещена в элемент , где она стала привязкой свойства, [ngIf].
- Остальная часть
, включая его атрибут класса, перемещена внутрь элемента .
Первая форма не фактически визуализирован, только готовый продукт попадает в DOM.
Angular использовал содержимое во время его фактического рендеринга и заменил диагностическим комментарием.
Директивы NgFor и NgSwitch … следуют одному шаблону. ngFor
Angular преобразует * ngFor аналогичным образом из синтаксиса звездочки (*) в элемент .
Вот полнофункциональное приложение NgFor, написано в обоих направлениях:
({{i}}) {{hero.name}}({{i}}) {{hero.name}}Вживую Демонстрация:
Это просто фрагмент кода, объясняющий конкретную концепцию, и может не иметь вывода
См. Pen ng-for от w3resource (@ w3resource) на CodePen.
Это явно сложнее, чем ngIf, и это правильно. Директива NgFor имеет больше функций, как обязательных, так и дополнительных, чем NgIf, показанный в этом руководстве. Как минимум, для NgFor требуется переменная цикла (let hero) и список (heroes).
Вы включаете эти функции в строке, присвоенной ngFor, которую вы пишете в микросинтаксисе Angular.
Все, что находится за пределами строки ngFor, остается с элементом хоста (
), когда перемещается внутри . В этом примере [ngClass] = «odd» остается наМикросинтаксис Angular позволяет сконфигурировать директиву в виде компактной удобной строки. Синтаксический анализатор микросинтаксиса переводит эту строку в атрибуты в :
- Ключевое слово let объявляет входную переменную шаблона, на которую вы ссылаетесь в шаблоне. Входными переменными в этом примере являются hero, i и odd. Синтаксический анализатор переводит let hero, let i и let odd в переменные с именами let-hero, let-i и let-odd.
- Синтаксический анализатор микросинтаксиса берет и trackBy, присваивая им заголовки (of -> Of, trackBy -> TrackBy) и ставит перед ними префикс имени атрибута директивы (ngFor), в результате чего получаются имена ngForOf и ngForTrackBy. Это имена двух входных свойств NgFor. Вот как директива узнает, что список — это герои, а функция отслеживания — trackById.
- По мере того, как директива NgFor просматривает список, она устанавливает и сбрасывает свойства своего собственного объекта контекста. Эти свойства включают index и odd, а также специальное свойство с именем $ implicit..
- Переменные let-i и let-odd были определены как let i = index и let odd = odd. Angular устанавливает для них текущее значение индекса контекста и нечетных свойств.
- Свойство контекста для let-hero не было указано. Его предполагаемый источник неявен. Angular устанавливает let-hero в значение свойства контекста $ implicit, которое NgFor инициализировал с помощью hero для текущей итерации.
- В руководстве по API описаны дополнительные свойства директивы NgFor и свойства контекста.
- NgFor реализуется директивой NgForOf.
Эти механизмы микросинтаксиса доступны вам, когда вы пишете свои собственные структурные директивы.
Входная переменная шаблона
Входная переменная шаблона — это переменная, на значение которой можно ссылаться в одном экземпляре шаблона. В этом примере есть несколько таких переменных: hero, i и odd. Всем предшествует ключевое слово let.
Входная переменная шаблона не то же самое, что ссылочная переменная шаблона, ни семантически, ни синтаксически.
Вы объявляете входной шаблон шаблона переменная с использованием ключевого слова let (let hero). Область действия переменной ограничена одним экземпляром повторяющегося шаблона. Вы можете снова использовать то же имя переменной в определении других структурных директив.
Вы объявляете ссылочную переменную шаблона, добавляя к имени переменной префикс # (#var). Ссылочная переменная относится к присоединенному к ней элементу, компоненту или директиве. К нему можно получить доступ в любом месте всего шаблона.
Входные и ссылочные имена переменных шаблона имеют свои собственные пространства имен. Герой в let hero никогда не является той же переменной, что и герой, объявленный как #hero.
Одна структурная директива для каждого элемента хоста
Когда-нибудь вы захотите повторить блок HTML, но только при выполнении определенного условия. Вы попытаетесь поместить как * ngFor, так и * ngIf в один и тот же элемент хоста. Angular вам не позволит. К элементу можно применить только одну структурную директиву.
Причина в простоте. Структурные директивы могут делать сложные вещи с основным элементом и его потомками. Когда две директивы претендуют на один и тот же хост-элемент, какая из них имеет приоритет? Что должно идти первым, NgIf или NgFor? Может ли NgIf отменить действие NgFor? Если так (а кажется, что так и должно быть), то как Angular должен обобщать возможность отмены для других структурных директив?
На эти вопросы нет простых ответов. Запрещение нескольких структурных директив делает их спорными. Для этого варианта использования есть простое решение: поместите * ngIf в элемент контейнера, который обертывает элемент * ngFor. Один или оба элемента могут быть ng-контейнером, поэтому вам не нужно вводить дополнительные уровни HTML
Внутри директив NgSwitch
Angular NgSwitch на самом деле представляет собой набор взаимодействующих директив: NgSwitch, NgSwitchCase и NgSwitchDefault.
Вот пример.
unknown-hero>Живая демонстрация:
Это просто фрагмент кода, объясняющий конкретная концепция и может не иметь вывода
См. Pen ng-switch от w3resource (@ w3resource) на CodePen.
Назначенное значение переключателя to NgSwitch (hero.emotion) определяет, какие (если есть) из вариантов переключения отображаются.
Сам NgSwitch не является структурной директивой. Это директива атрибута, которая контролирует поведение двух других директив переключателя. Вот почему вы пишете [ngSwitch], а не * ngSwitch.
NgSwitchCase и NgSwitchDefault — это структурные директивы. Вы прикрепляете их к элементам с помощью обозначения префикса звездочки (*). NgSwitchCase отображает свой хост-элемент, когда его значение совпадает со значением переключателя. NgSwitchDefault отображает свой хост-элемент, когда ни один из соседних NgSwitchCase не соответствует значению переключателя.
Элемент, к которому вы применяете директиву, является его хост-элементом. — это элемент хоста для счастливого * ngSwitchCase. — это элемент хоста для * ngSwitchDefault.
Как и другие структурные директивы, NgSwitchCase и NgSwitchDefault можно обессахаривать в форме элемента .
hero>Живая демонстрация:
Это просто фрагмент кода, объясняющий конкретную концепцию, и может не иметь никакого вывода
См. перо ng-switch-Templete от w3resource (@ w3resource) на CodePen.
Предпочитайте синтаксис звездочки (*).
Синтаксис звездочки (*) более понятен, чем обессахаренная форма. Используйте , когда нет единственного элемента для размещения директивы.
Хотя редко есть веская причина для применения структурной директивы в атрибуте шаблона или форме элемента, все же важно знать, что Angular создает , и понимать, как это работает. Вы будете обращаться к при написании собственной структурной директивы.
элемент Angular для рендеринга HTML. Он никогда не отображается напрямую. Фактически, перед рендерингом представления Angular заменяет и его содержимое комментарием.
Если нет структурной директивы, и вы просто оборачиваете некоторые элементы в эти элементы исчезают. Такова судьба среднего «Хипа!» во фразе «Хип! Хип! Ура!».
Хип!
Хип!
Ура!
Живая демонстрация:
Это просто фрагмент кода, объясняющий конкретную концепцию и не имеющий вывода
См. Pen YbwGxd от w3resource (@ w3resource) на CodePen.
Angular стирает среднее «Hip!», Оставляя приветствие менее восторженным.
Структурная директива заставляет работать, как вы увидите, когда напишете свою собственную структурную директиву.
Сгруппируйте родственные элементы с помощью
Часто существует корневой элемент, который может и должен содержать структурную директиву. Элемент списка (
- ) является типичным элементом хоста повторителя NgFor.
src/app/app.component.html (ngfor-li)
- {{hero.name}}
Когда нет хост-элемента, обычно вы можете обернуть содержимое в собственный элемент контейнера HTML, например
, и прикрепить директиву к этой оболочке.{{hero.name}}Представляем другой элемент контейнера? обычно или
? для группировки элементов под единственный корень обычно безвреден. Обычно … но не всегда.Элемент группировки может нарушить внешний вид шаблона, потому что стили CSS не ожидают и не поддерживают новый макет. Например, предположим, что у вас есть следующий макет абзаца.
Я повернул за угол и увидел {{hero.name}} . Я помахал и продолжил свой путь.
Live Demo:
Это просто фрагмент кода, объясняющий конкретную концепцию, и может не иметь вывода
См. Pen ng-if-span от w3resource (@ w3resource) на CodePen.
У вас также есть правило стиля CSS, которое применяется к внутри абзаца
.
p span {цвет: красный; размер шрифта: 70%; }
Построенный абзац выглядит странно.
Стиль p span, предназначенный для использования в другом месте, был случайно применен здесь.
Другая проблема: некоторые элементы HTML требуют, чтобы все непосредственные дочерние элементы были определенного типа. Например, элемент требует дочерних элементов . Вы не можете заключить параметры в условный
или .Когда вы попробуете это,
Выберите своего любимого героя (){{h.name}} ( {{h.emotion}})Живая демонстрация:
Это просто фрагмент кода, объясняющий конкретную концепцию, и может не иметь вывода
См. Pen dEGpdQ от w3resource (@ w3resource) на CodePen.
Браузер не отображает в .
на помощь
Angular — это группирующий элемент который не мешает стилям или макету, потому что Angular не помещает его в DOM.
Вот опять условный абзац, это раз, используя .
Я повернул за угол и увидел {{hero.name}}. Я помахал и продолжил свой путь.
Живая демонстрация:
Это просто фрагмент кода, объясняющий конкретную концепцию, и может не иметь вывода
См. Pen MdKjXo от w3resource (@ w3resource) на CodePen.
Он отображается правильно.
Теперь условно исключить выбор с .
Выберите своего любимого героя (){{h.name}} ({{h.emotion}})Живая демонстрация:
Это просто фрагмент кода, объясняющий конкретная концепция и может не иметь вывода
См. Pen QRyMVK от w3resource (@ w3resource) на CodePen.
Выпадающий список работает правильно
Примечание: помните, что директива ngModel определена как часть Angular FormsModule, и вы необходимо включить FormsModule в раздел import: […] метаданных модуля Angular, в котором вы хотите его использовать.
— это элемент синтаксиса, распознаваемый Angular парсер. Это не директива, компонент, класс или интерфейс. Это больше похоже на фигурные скобки в if-блоке JavaScript:
if (someCondition) {statement1; оператор2; statement3;}
Демонстрация:
Это просто фрагмент кода, объясняющий конкретную концепцию, и не может иметь какой-либо вывод
См. Pen oRbeVm от w3resource (@ w3resource) на CodePen.
Без этих фигурных скобок JavaScript будет выполнять только первую инструкцию когда вы намереваетесь условно выполнить их все как один блок. удовлетворяет аналогичную потребность в шаблонах Angular.
Напишите структурную директиву
В этом разделе вы напишите структурную директиву UnlessDirective, которая делает противоположность NgIf. NgIf отображает содержимое шаблона, когда условие истинно. UnlessDirective отображает содержимое, когда условие ложно.
src/app/app.component.html (appUnless-1)
Показать это предложение если условие не выполняется.
Live Demo:
Это просто фрагмент кода объяснение конкретной концепции и может не иметь вывода
См. Pen wbMqbG от w3resource (@ w3resource) на CodePen.
Создание директивы — это аналогично созданию компонента.
- Импортируйте декоратор Directive (вместо декоратора Component).
- Импортируйте символы Input, TemplateRef и ViewContainerRef ; они понадобятся вам для любой структурной директивы.
- Примените декоратор к классу директивы.
- Установите селектор атрибутов CSS, который идентифицирует директиву при применении к элементу в шаблоне.
Вот с чего можно начать:
import {Directive, Input, TemplateRef, ViewContainerRef} from '@ angular/core '; @ Directive ({selector:' [appUnless] '}) класс экспорта UnlessDirective {}
Live Demo:
Это просто фрагмент кода, объясняющий конкретную концепцию, и может не иметь никакого вывода
См. Pen vwLJqo от w3resource (@ w3resource) на CodePen.
Селектор директивы обычно представляет собой имя атрибута директивы в квадратных скобках, [appUnless]. Скобки определяют селектор атрибутов CSS.
Имя атрибута директивы должно быть написано в lowerCamelCase и начинаться с префикса. Не используйте ng. Этот префикс принадлежит Angular. Выберите что-нибудь короткое, что подходит вам или вашей компании. В этом примере префиксом является приложение.
Имя класса директивы заканчивается на Directive согласно руководству по стилю. Собственные директивы Angular этого не делают.
TemplateRef и ViewContainerRef
Простая структурная директива, подобная этой, создает встроенное представление из Angular -generated и вставляет это представление в контейнер представления рядом с исходным элементом
host директивы.
Вы получите содержимое с TemplateRef и доступ к контейнеру представления через aViewContainerRef.
Вы вводите обе в конструктор директив как частные переменные класса.
copyconstructor (private templateRef: TemplateRef , private viewContainer: ViewContainerRef) {}
Живая демонстрация:
Это просто код фрагмент, объясняющий конкретную концепцию и не имеющий вывода
См. Pen xNZXKy от w3resource (@ w3resource) на CodePen.
Свойство appUnless
Потребитель директивы ожидает привязать условие true/false к [appUnless]. Это означает, что директиве требуется свойство appUnless, украшенное @Input
@Input () set appUnless (condition: boolean) {if (! Condition &&! This.hasView) { this.viewContainer.createEmbeddedView (this.templateRef); this.hasView = true; } иначе, если (условие && this.hasView) {this.viewContainer.clear (); this.hasView = false; }}
Живая демонстрация:
Это просто фрагмент кода, объясняющий конкретную концепцию, и может не иметь любой вывод
См. Pen wbMrBz от w3resource (@ w3resource) на CodePen.
Angular устанавливает свойство appUnless всякий раз, когда значение условия изменяется . Поскольку свойство appUnless действительно работает, ему требуется установщик.
- Если условие ложное и представление не было создано ранее, сообщите контейнеру представления о создании встроенного представления. из шаблона.
- Если условие истинно и представление в настоящее время отображается, очистите контейнер, который также разрушает представление.
Никто не читает appUnless, поэтому ему не нужен геттер.
Завершенный код директивы выглядит следующим образом:
import {Directive, Input, TemplateRef, ViewContainerRef} from '@ angular/core';/** * Добавить содержимое шаблона в DOM, если условие не выполняется. */@ Директива ({selector: '[appUnless]'}) класс экспорта UnlessDirective {private hasView = false; конструктор (private templateRef: TemplateRef , private viewContainer: ViewContainerRef) {} @Input () set appUnless (condition: boolean) {if (! condition &&! this.hasView) {this.viewContainer.createEmbeddedView (this.templateRef) ; this.hasView = true; } иначе, если (условие && this.hasView) {this.viewContainer.clear (); это. hasView = false; }}}
Живая демонстрация:
Это просто фрагмент кода, объясняющий конкретную концепцию, и не может иметь какой-либо вывод
См. Pen ardLOR от w3resource (@ w3resource) на CodePen.
Добавьте эту директиву в массив объявлений AppModule.
Затем создайте HTML-код, чтобы попробовать это.
(A) This абзац отображается, потому что условие ложно.
(B) Хотя условие истинно, этот абзац отображается, потому что appUnless имеет значение false.
Живая демонстрация:
Это просто фрагмент кода, объясняющий конкретную концепцию, и не может есть какой-либо вывод
См. Pen WBrZQp от w3resource (@ w3resource) на CodePen.
Если условие ложное, верх (A) появится абзац, а нижний (B) абзац исчезнет. Если условие истинно, верхний (A) абзац удаляется и появляется нижний (B) абзац.