Использование web-компонентов в Angular
Веб-компоненты позволяют нам создавать переиспользуемые, настраиваемые элементы. Основное преимущество веб-компонентов - это их интероперабельность: поскольку они поддерживаются нативно в браузерах, веб-компоненты могут использоваться в любой HTML-среде, с любым фреймворком или вообще без фреймворка.
Одним из основных аспектов веб-компонентов является инкапсуляция. Вы можете разделять и скрывать структуру разметки, стиль и поведение, чтобы различные части страницы не конфликтовали между собой.
Поэтому веб-компоненты идеально подходят для разработки дизайн-систем, компонентов, которые можно использовать повторно, а так же встраиваемых виджетов. Давайте посмотрим, как использовать нативный веб-компонент с лучшими библиотеками в отрасли:
Создание веб-компонентов с Lit
Создание веб-компонента с нуля на чистом JavaScript может быстро превратиться в легаси, который сложно масштабировать и поддерживать. К счастью, мы можем создавать быстрые, легкие веб-компоненты с помощью Lit.
В основе Lit лежит базовый класс компонентов, который убивает необходимость в шаблонах, предоставляя реактивное состояние, стили с областью видимости и декларативную систему шаблонов.
Хотя мы будем использовать Lit, я рекомендую изучить, как разрабатывать веб-компоненты с использованием нативного JavaScript.
Для начала реализуем простейший Select, который будет работать через нативный API. Вопросы сборщиков здесь обсуждаться не будут, можно использовать любой.
import { css, html, LitElement } from 'lit';
export class SelectElement<T extends Record<string, unknown>> extends LitElement {
static styles = css`
button[active] {
background-color: honeydew
}
`
@property({ type: Array })
data: T[] = [];
@property({ attribute: 'id-key' })
idKey: keyof T = 'id' as keyof T;
@property({ attribute: 'val-key' })
valKey: keyof T = 'label' as keyof T;
@property({ state: true })
private activeItem: T | null = null;
render() {
return html``
}
}
customElements.define('ui-select', SelectElement);Мы создаем SelectElement и определяем три входных параметра — data, idKey и valKey. Мы также определяем состояние activeItem для отслеживания текущего выбранного элемента. Добавим шаблон:
import { css, html, LitElement } from 'lit';
import { repeat } from 'lit/directives/repeat.js';
export class SelectElement extends LitElement {
...
selectItem(item: T) {
this.activeItem = item;
const event = new CustomEvent<T>('select-item', {
detail: item,
bubbles: true,
composed: true
});
this.dispatchEvent(event);
}
render() {
return html`
<p>Active: ${this.activeItem ? this.activeItem[this.valKey] : 'None' }</p>
${repeat(
this.data,
current => current[this.idKey],
current => html`
<button ?active=${this.activeItem?.[this.idKey] === current[this.idKey]}
@click=${() => this.selectItem(current)}>
${current[this.valKey]}
</button>
`)}
`
}
}
customElements.define('ui-select', SelectElement);
Мы используем директиву repeat для эффективного рендеринга наших элементов. Она принимает три параметра - collection, keyFunction, которая принимает элемент в качестве аргумента и возвращает уникальный ключ для него, и itemTemplate, которая принимает элемент и его текущий индекс в качестве аргументов и возвращает TemplateResult.
При щелчке на элементе он помечается как активный и отправляется пользовательское событие, на которое родитель может подписаться.
Мы используем хорошую возможность из Lit, называемую выражениями для атрибутов boolean. Атрибут active будет добавлен или удален в зависимости от результата выражения. Мы используем его для стилизации активного элемента.
Если вы ищете генератор lit-файлов, то может пригодиться эта библиотека.
Использование веб-компонентов в Angular
Сначала нужно использовать схему CUSTOM_ELEMENTS_SCHEMA. Angular игнорирует пользовательские элементы (названные с дефисами), которые он не распознает, вместо того чтобы генерировать ошибку.
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { TodosComponent } from './todos.component';
@NgModule({
declarations: [TodosComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class TodosModule {}Теперь можно использовать веб-компонент ui-select внутри компонента TodosComponent:
import '@org/ui/lib/select';
import { Component } from '@angular/core';
import { Todo, randTodo } from '@ngneat/falso';
@Component({
selector: 'app-todos',
template: `
<button (click)="replace()">Replace</button>
<ui-select [data]="todos"
(select-item)="onSelect($event)"
val-key="title">
</ui-select>
`
})
export class TodosComponent {
todos: Todo[] = randTodo({ length: 3 });
replace() {
this.todos = randTodo({ length: 3 });
}
onSelect(e: CustomEvent<Todo>) {
console.log(e.detail);
}
}Как показывает пример, интеграция Angular с веб-компонентами работает из коробки. Angular связывает свойство компонента todos со свойством данных в элементе ui-select. Привязка событий слушает событие select-item и вызывает метод onSelectItem() компонента всякий раз, когда оно срабатывает.
Это вольный перевод материала