Все, что вам нужно знать о ng-template, ng-content, ng-container и * ngTemplateOutlet в Angular

Это был один из тех дней, когда я был занят работой над новыми функциями для своего офисного проекта. Вдруг что-то привлекло мое внимание:

Изучая DOM, я увидел, ngcontentчто Angular применяет его к элементам. Хм ... если они содержат элементы в окончательной модели DOM, тогда какой от них смысл ? В то время я запутался между и .

Стремясь узнать ответы на свои вопросы, я открыл для себя концепцию . К моему удивлению, тоже было *ngTemplateOutlet. Я начал свое путешествие в поисках ясности в отношении двух концепций, но теперь у меня их четыре, звучащих почти одинаково!

Вы когда-нибудь были в такой ситуации? Если да, то вы попали в нужное место. Итак, без лишних слов, давайте рассмотрим их по очереди.

1.

Как подсказывает название , которое является шаблоном элемент , который использует угловые со структурными директивами ( *ngIf, *ngFor, [ngSwitch]и пользовательские директивы).

Эти элементы шаблона работают только при наличии структурных директив . Angular оборачивает элемент хоста (к которому применяется директива) внутрьи использует егов готовой DOM, заменяя его диагностическими комментариями.

Рассмотрим простой пример *ngIf:

Выше показана угловая интерпретация *ngIf. Angular помещает хост-элемент, к которому применяется директива, и сохраняет хост как есть. Последний DOM похож на то, что мы видели в начале этой статьи:

Применение:

Мы видели, как использует Angular, но что, если мы захотим его использовать? Поскольку эти элементы работают только со структурной директивой, мы можем написать так:

Вот homeэто booleanсвойство компонента набора к trueзначению. Вывод приведенного выше кода в DOM:

Ничего не получилось! :(

Но почему мы не можем увидеть наше сообщение даже после правильного использования со структурной директивой?

Это был ожидаемый результат. Как мы уже говорили, Angular заменяет диагностические комментарии. Несомненно, приведенный выше код не вызовет никаких ошибок, поскольку Angular идеально подходит для вашего варианта использования. Вы никогда не узнаете, что именно произошло за кулисами.

Давайте сравним две вышеуказанные модели DOM, которые были отрисованы с помощью Angular:

Если вы внимательно посмотрите, в последней модели DOM примера 2 есть один дополнительный тег комментария . Код, который интерпретировал Angular, был:

Angular обернул ваш хост внутри другого и преобразовал не только внешний в диагностический, но и внутренний! Вот почему вы не смогли увидеть ни одного своего сообщения.

Чтобы избавиться от этого, есть два способа получить желаемый результат:

Способ 1:

В этом методе вы предоставляете Angular формат без сахара, который не требует дальнейшей обработки. На этот раз Angular преобразует только в комментарии, но оставляет содержимое внутри нетронутым (они больше не внутри, как в предыдущем случае). Таким образом, он будет правильно отображать контент.

Чтобы узнать больше о том, как использовать этот формат с другими структурными директивами, обратитесь к этой статье.

Способ 2:

Это совершенно незаметный формат и редко используется (с использованием двух братьев и сестер ). Здесь мы даем ссылку шаблонной *ngIfв ее thenсказать ему , какой шаблон следует использовать , если условие истинно.

Подобное использование нескольких не рекомендуется (вы можете использовать вместо этого), поскольку это не то, для чего они предназначены. Они используются в качестве контейнера для шаблонов, которые можно повторно использовать в нескольких местах. Подробнее об этом мы поговорим в следующем разделе этой статьи.

2.

Вы когда-нибудь писали или видели код, похожий на этот:

Причина, по которой многие из нас пишут этот код, заключается в невозможности использовать несколько структурных директив для одного элемента хоста в Angular. Теперь этот код работает нормально, но он вводит несколько дополнительных пустых значений в DOM, если item.idэто ложное значение, которое может не требоваться.

Можно не беспокоиться о таком простом примере, как этот, но для огромного приложения со сложной DOM (для отображения десятков тысяч данных) это может стать проблемой, поскольку к элементам могут быть прикреплены слушатели, которые все еще будут присутствовать в DOM слушает события.

Что еще хуже, так это уровень вложенности, который вам нужно сделать, чтобы применить свой стиль (CSS)!

Не беспокойтесь, мы должны прийти на помощь!

Angular - это группирующий элемент, который не мешает стилям или макету, потому что Angular не помещает его в DOM .

Итак, если мы напишем наш Пример 1 с помощью :

Мы получаем окончательный DOM как:

Смотрите, мы избавились от этих пустых s. Мы должны использовать, когда просто хотим применить несколько структурных директив, не вводя никаких дополнительных элементов в нашу DOM.

Для получения дополнительной информации обратитесь к документации. Есть еще один вариант использования, когда он используется для динамического внедрения шаблона на страницу. Я расскажу об этом варианте использования в последнем разделе этой статьи.

3.

Они используются для создания настраиваемых компонентов. Это означает, что компоненты могут быть настроены в зависимости от потребностей пользователя. Это хорошо известно как проекция контента . Компоненты, которые используются в опубликованных библиотеках, используются, чтобы сделать себя настраиваемыми.

Рассмотрим простой компонент:

Содержимое HTML, передаваемое в открывающем и закрывающем тегах компонента, является содержимым, которое будет проецироваться. Это то, что мы называем проекцией контента . Контент будет отображаться внутри компонента. Это позволяет потребителю компонента передавать любой настраиваемый нижний колонтитул внутри компонента и точно контролировать , как он хочет, чтобы он отображался.

Множественные прогнозы:

Что, если бы вы могли решить, какой контент и где разместить? Вместо каждого содержимого, проецируемого внутри одного , вы также можете управлять тем, как содержимое будет проецироваться с помощью selectатрибута . Чтобы решить, какой контент проецировать внутри конкретного элемента, необходим селектор .

Вот как:

Мы изменили определение для выполнения мульти-контентной проекции. selectАтрибут выбирает тип контента , который будет оказан внутри конкретным . Здесь нам нужно сначала selectотобразить h1элемент заголовка . Если у проецируемого контента нет h1элементов, он ничего не будет отображать. Точно так же второй selectищет файл div. Остальной контент отображается внутри последнего без номера select.

Вызов компонента будет выглядеть так:

4. * ngTemplateOutlet

… Они используются в качестве контейнера для шаблонов, которые можно повторно использовать в нескольких местах. Подробнее об этом мы поговорим в следующем разделе этой статьи.

… Есть еще один вариант использования, когда он используется для динамического внедрения шаблона на страницу. Я расскажу об этом варианте использования в последнем разделе этой статьи.

В этом разделе мы обсудим два вышеупомянутых момента. *ngTemplateOutletиспользуется для двух сценариев - для вставки общего шаблона в различные разделы представления независимо от циклов или условий и для создания хорошо настроенного компонента.

Повторное использование шаблона:

Представьте себе представление, в котором вам нужно вставить шаблон в нескольких местах. Например, логотип компании для размещения на веб-сайте. Мы можем добиться этого, написав шаблон для логотипа один раз и повторно используя его повсюду в представлении.

Ниже приведен фрагмент кода:

Как видите, мы всего лишь один раз написали шаблон логотипа и использовали его трижды на одной странице с помощью одной строчки кода!

*ngTemplateOutletтакже принимает объект контекста, который может быть передан для настройки общего вывода шаблона. Для получения дополнительных сведений об объекте контекста обратитесь к официальной документации.

Настраиваемые компоненты:

Второй вариант использования *ngTemplateOutlet- это сильно настраиваемые компоненты. Рассмотрим наш предыдущий пример компонента с некоторыми изменениями:

Выше модифицированная версия компонента , который принимает три входных свойства -  headerTemplate, bodyTemplate, footerTemplate. Ниже приведен фрагмент для project-content.ts:

Здесь мы пытаемся показать верхний, основной и нижний колонтитулы, полученные от родительского компонента . Если какой-либо из них не указан, наш компонент покажет на его месте шаблон по умолчанию. Таким образом, создается настраиваемый компонент.

Чтобы использовать наш недавно измененный компонент:

Вот как мы собираемся передать ссылки шаблона нашему компоненту. Если какой-либо из них не передан, компонент отобразит шаблон по умолчанию.

ng-content против * ngTemplateOutlet

Оба они помогают нам создавать компоненты с индивидуальными требованиями, но что выбрать и когда?

Это ясно видно, что *ngTemplateOutletдает нам больше возможностей для отображения шаблона по умолчанию, если он не предоставлен.

Это не так с ng-content. Он отображает контент как есть. Максимально вы можете разделить контент и отобразить его в разных местах вашего представления с помощью selectатрибута. Вы не можете условно отобразить содержимое внутри ng-content. Вы должны показывать контент, полученный от родителя, без возможности принимать решения на основе контента.

Однако выбор одного из двух полностью зависит от вашего варианта использования. По крайней мере, теперь *ngTemplateOutletв нашем арсенале есть новое оружие, которое обеспечивает больший контроль над контентом в дополнение к функциям ng-content!