Обзор элементов Angular
Элементы Angular — это компоненты Angular, упакованные в виде пользовательских элементов (также называемых веб-компонентами), веб-стандарта для определения новых элементов HTML независимым от фреймворка способом.
Пользовательские элементы — это функция веб-платформы, доступная во всех браузерах, поддерживаемых Angular. Пользовательский элемент расширяет HTML, позволяя определить тег, содержимое которого создаётся и контролируется кодом JavaScript. Браузер поддерживает CustomElementRegistry определённых пользовательских элементов, в котором класс JavaScript, допускающий создание экземпляров, сопоставляется с тегом HTML.
Пакет @angular/elements экспортирует API createCustomElement(), который обеспечивает связь между интерфейсом компонента Angular и функцией обнаружения изменений и встроенным API DOM.
Преобразование компонента в пользовательский элемент делает всю необходимую инфраструктуру Angular доступной браузеру. Создать пользовательский элемент просто: он автоматически связывает представление, определённое компонентом, с обнаружением изменений и привязкой данных, сопоставляя функции Angular с соответствующими встроенными возможностями HTML.
Использование пользовательских элементов
Пользовательские элементы загружаются самостоятельно: они начинают работу при добавлении в DOM и уничтожаются при удалении из DOM. После добавления пользовательского элемента в DOM на любой странице он выглядит и ведёт себя как любой другой элемент HTML и не требует специальных знаний терминологии Angular или правил его использования.
Чтобы добавить пакет @angular/elements в рабочую область, выполните следующую команду:
Принцип работы
Функция createCustomElement() преобразует компонент в класс, который можно зарегистрировать в браузере как пользовательский элемент. Зарегистрировав настроенный класс в реестре пользовательских элементов браузера, используйте новый элемент так же, как встроенный элемент HTML, в содержимом, которое добавляете непосредственно в DOM:
<my-popup message="Use Angular!"></my-popup>
Когда пользовательский элемент помещается на страницу, браузер создаёт экземпляр зарегистрированного класса и добавляет его в DOM. Содержимое задаётся шаблоном компонента, в котором используется синтаксис шаблонов Angular, и отображается с помощью данных компонента и DOM. Входные свойства компонента соответствуют входным атрибутам элемента.
Преобразование компонентов в пользовательские элементы
Angular предоставляет функцию createCustomElement() для преобразования компонента Angular вместе с его зависимостями в пользовательский элемент.
В результате преобразования реализуется интерфейс NgElementConstructor и создаётся класс-конструктор, настроенный для создания самостоятельно загружаемого экземпляра компонента.
Используйте встроенную функцию браузера customElements.define(), чтобы зарегистрировать настроенный конструктор и связанный с ним тег пользовательского элемента в CustomElementRegistry браузера. Когда браузер встречает тег зарегистрированного элемента, он использует конструктор для создания экземпляра пользовательского элемента.
ВАЖНО: Не используйте селектор компонента в качестве имени тега пользовательского элемента. Это может привести к неожиданному поведению, поскольку Angular создаст два экземпляра компонента для одного элемента DOM: один обычный компонент Angular и второй — с использованием пользовательского элемента.
Сопоставление
Пользовательский элемент содержит компонент Angular и служит связующим звеном между данными и логикой, определёнными в компоненте, и стандартными API DOM. Свойства и логика компонента напрямую сопоставляются с атрибутами HTML и системой событий браузера.
-
API создания анализирует компонент, чтобы найти входные свойства, и определяет соответствующие атрибуты пользовательского элемента. Имена свойств преобразуются в формат, совместимый с пользовательскими элементами, которые не различают регистр. В полученных именах атрибутов используются строчные буквы, разделённые дефисами. Например, для компонента с
inputProp = input({alias: 'myInputProp'})соответствующий пользовательский элемент определяет атрибутmy-input-prop. -
Выходные данные компонента отправляются как HTML-события пользовательских событий, причём имя пользовательского события совпадает с именем выходного параметра. Например, для компонента с
valueChanged = output()соответствующий пользовательский элемент отправляет события с именем "valueChanged", а отправленные данные сохраняются в свойствеdetailсобытия. Если указать псевдоним, используется он; например,clicks = output<string>({alias: 'myClick'});приводит к отправке событий с именем "myClick".
Дополнительные сведения см. в документации по веб-компонентам в разделе Создание пользовательских событий.
Пример: сервис всплывающих окон
Раньше, чтобы добавлять компонент в приложение во время выполнения, нужно было определить динамический компонент, а затем загрузить его, прикрепить к элементу DOM и настроить все зависимости, обнаружение изменений и обработку событий.
Использование пользовательского элемента Angular упрощает и делает более прозрачным этот процесс, автоматически предоставляя всю инфраструктуру и средства фреймворка: вам остаётся только определить нужный тип обработки событий. (Если вы не собираетесь использовать компонент в приложении, его всё же нужно исключить из компиляции.)
В следующем примере приложения сервиса всплывающих окон определён компонент, который можно загрузить динамически или преобразовать в пользовательский элемент.
| Файлы | Описание |
|---|---|
popup.ts | Определяет простой всплывающий элемент, который отображает входное сообщение и содержит анимацию и стили. |
popup.service.ts | Создаёт внедряемый сервис, предоставляющий два способа вызвать Popup: как динамический компонент или как пользовательский элемент. Обратите внимание, насколько больше настроек требуется для динамической загрузки. |
app.ts | Определяет корневой компонент приложения, который использует PopupService для добавления всплывающего окна в DOM во время выполнения. При запуске приложения конструктор корневого компонента преобразует Popup в пользовательский элемент. |
Для сравнения в демонстрации показаны оба способа. Одна кнопка добавляет всплывающее окно с помощью динамической загрузки, а другая использует пользовательский элемент. Результат одинаков, но подготовка отличается.
Типизация пользовательских элементов
Общие API DOM, такие как document.createElement() или document.querySelector(), возвращают тип элемента, соответствующий указанным аргументам. Например, вызов document.createElement('a') возвращает HTMLAnchorElement, у которого, как знает TypeScript, есть свойство href. Аналогично, document.createElement('div') возвращает HTMLDivElement, у которого, как знает TypeScript, нет свойства href.
При вызове для неизвестных элементов, например пользовательского элемента (в нашем примере это popup-element), методы возвращают общий тип, например HTMLElement, поскольку TypeScript не может определить правильный тип возвращаемого элемента.
Пользовательские элементы, созданные с помощью Angular, расширяют NgElement (который, в свою очередь, расширяет HTMLElement). Кроме того, у таких пользовательских элементов будет свойство для каждого входного параметра соответствующего компонента. Например, у нашего popup-element есть свойство message типа string.
Если нужно получить правильные типы для пользовательских элементов, есть несколько вариантов. Предположим, вы создаёте пользовательский элемент my-dialog на основе следующего компонента:
@Component(/* ... */)
class MyDialog {
content = input('');
}
Самый простой способ получить точную типизацию — привести возвращаемое значение соответствующих методов DOM к правильному типу. Для этого используйте типы NgElement и WithProperties (оба экспортируются из @angular/elements):
const aDialog = document.createElement('my-dialog') as NgElement &
WithProperties<{content: string}>;
aDialog.content = 'Hello, world!';
aDialog.content = 123; // <-- ERROR: TypeScript knows this should be a string.
aDialog.body = 'News'; // <-- ERROR: TypeScript knows there is no `body` property on `aDialog`.
Это хороший способ быстро получить такие возможности TypeScript, как проверка типов и автодополнение, для пользовательского элемента. Однако он может быть неудобен, если требуется использовать его в нескольких местах, поскольку при каждом обращении необходимо приводить возвращаемый тип.
Альтернативный способ, при котором тип каждого пользовательского элемента нужно определить только один раз, — расширить HTMLElementTagNameMap. TypeScript использует его, чтобы определить тип возвращаемого элемента по имени его тега (для таких методов DOM, как document.createElement(), document.querySelector() и т. д.):
declare global {
interface HTMLElementTagNameMap {
'my-dialog': NgElement & WithProperties<{content: string}>;
'my-other-element': NgElement & WithProperties<{foo: 'bar'}>;
…
}
}
Теперь TypeScript может определять правильный тип так же, как и для встроенных элементов:
document.createElement('div'); //--> HTMLDivElement (built-in element)
document.querySelector('foo'); //--> Element (unknown element)
document.createElement('my-dialog'); //--> NgElement & WithProperties<{content: string}> (custom element)
document.querySelector('my-other-element'); //--> NgElement & WithProperties<{foo: 'bar'}> (custom element) Ограничения
При уничтожении и последующем повторном добавлении пользовательских элементов, созданных с помощью @angular/elements, следует соблюдать осторожность из-за проблем с обратным вызовом disconnect(). С этой проблемой можно столкнуться в следующих случаях:
- При отображении компонента в
ng-ifилиng-repeatвAngularJS - При ручном отсоединении элемента от DOM и его повторном добавлении
Super-powered by Google ©2010–2025.
Code licensed under an MIT-style License. Documentation licensed under CC BY 4.0.
https://angular.dev/guide/elements