Внедрение HTML в компонент с использованием Angular может быть выполнено с помощью атрибута «innerHTML» контейнера (обычно «div» или «p»). Однако у этого подхода есть две проблемы:

  • Если компонент определяет некоторые стили CSS (и использует инкапсуляцию представления по умолчанию), они не будут применяться к вставленному HTML.
  • Если HTML содержит некоторые внутренние ссылки, они перезагрузят все приложение вместо навигации с помощью маршрутизатора.

Я разработал это приложение на Angular, чтобы продемонстрировать эти проблемы. Основные части этого приложения:

  • Он содержит три компонента, загружаемых через конфигурацию маршрутизатора («/ home», «/ hello» и «/ bye»).
  • «Главный» компонент загружается по маршруту «/ home» и просто вставляет некоторый HTML в «div».
  • Компоненты «hello» и «bye» просто отображают их имя и обратную ссылку, чтобы вернуться на маршрут «/ home».

Как видите, HTML, используемый основным компонентом, использует «жирный» класс CSS, объявленный в стилях компонента. Кроме того, он содержит три ссылки: две внутренние (одна относительная и одна абсолютная) и внешняя.

Обычный способ отображения этого HTML-кода в компоненте - тот, который используется по умолчанию в StackBlitz: атрибут «innerHTML». Если вы это сделаете, результат будет следующим:

Как видите, первая проблема заключается в том, что класс «жирный» не применяется к первому абзацу. Более того, если вы нажмете на две первые ссылки, все приложение перезагрузится, поскольку вместо маршрутизатора Angular используется навигация браузера.

Эти две проблемы можно решить, используя вместо этого директиву« html ».

Вы можете проверить это, используя его вместо «innerHTML», чтобы увидеть следующий результат:

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

Вся логика директивы определена в ловушке «ngOnChanges», чтобы применять ее, как только значение ввода «html» изменится. Мы могли бы использовать сеттер для ввода «html», но поскольку здесь только один ввод, он не сильно меняет.

Первое, что мы делаем, это получаем уникальный идентификатор компонента. Действительно, при использовании инкапсуляции представления по умолчанию стили CSS фактически изменяются с помощью суффикса, чтобы охватить их компонентом. Например, «жирный» класс CSS фактически определен в исходном коде страницы следующим образом:

Вот почему использование innerHTML не работает для CSS. Действительно, к вставленному абзацу не будет применен атрибут «_ngcontent-lnp-c5», поэтому класс не будет применяться. Вот почему первое, что мы делаем в директиве, - это извлекаем имя этого атрибута для элемента, к которому применяется директива, и сохраняем его в переменной «_uniqueId».

Затем мы просто обновляем свойство «innerHTML» элемента, чтобы вставить HTML. Обратите внимание, что мы не дезинфицируем его, поэтому могут возникнуть некоторые риски инъекций, это вам решать. Я не проверял, но полагаю, что использование дезинфицирующего средства Angular для защиты директивы не должно быть настолько сложным.

Теперь, когда у нас есть имя атрибута, мы получаем ссылку на всех потомков элемента и добавляем к ним этот атрибут. Таким образом, абзац теперь будет соответствовать запросу CSS для класса «полужирный»:

Это решает проблему с CSS, но не со ссылками. Эта часть обрабатывается условием проверки того, является ли имя дочернего тега «A» или нет. Если это так, мы получаем значение его свойства «href». Обратите внимание, что мы не используем функцию getAttribute, так как она вернет необработанное значение. Я имею в виду, что функция «getAttribute» возвращает относительный URL-адрес для относительных ссылок, а свойство «href» возвращает абсолютный URL-адрес для всех ссылок. Следовательно, использование свойства вместо атрибута гарантирует, что URL-адрес всегда будет абсолютным.

Поскольку мы хотим использовать маршрутизатор Angular только для внутренних ссылок, мы проверяем, что свойство «href» начинается с источника страницы. Если это так, мы определяем событие «click» для элемента «a», чтобы перенаправить пользователя с помощью маршрутизатора и предотвратить поведение по умолчанию, чтобы браузер не перенаправлял пользователя на URL-адрес впоследствии.

Обратите внимание, что мы делаем URL-адрес относительным, прежде чем передавать его функции «навигации» маршрутизатора, поскольку он не поддерживает абсолютный URL-адрес.

И вот, теперь вы можете вставлять HTML в компоненты, которые:

  • Учитывает стили с областью видимости компонентов
  • Используйте роутер Angular для внутренних ссылок
  • Обходите дезинфекцию HTML (что может быть хорошо или плохо, решать вам).

Не стесняйтесь оставлять комментарии, если видите способы улучшить эту директиву.

Больше контента на plainenglish.io