Эта статья — перевод оригинальной статьи Luis Aviles "How to integrate Web Components using Lit in Angular"
Также я веду телеграм канал “Frontend по-флотски”, где рассказываю про интересные вещи из мира разработки интерфейсов.
Вступление
В этом руководстве я объясню необходимые шаги для интеграции веб-компонентов в Angular. Спойлер: Вы можете найти исходный код демонстрационного проекта в конце.

Настройка проекта
В вашей локальной среде должны быть установлены следующие инструменты:
Node.js. Желательно последняя версия LTS.
Менеджер пакетов. Вы можете использовать npm или yarn. В этом руководстве будет использоваться npm.
Создание проекта Angular
Давайте создадим проект с нуля с помощью Angular CLI.
ng new angular-lit-web-components --routing --prefix corp --style css --skip-tests
Эта команда инициализирует базовый проект с использованием некоторых параметров конфигурации:
--routing. Будет создан модуль маршрутизации.--prefix corp. Он определяет префикс, который будет применяться к селекторам для созданных компонентов (в данном случаеcorp). Значение по умолчанию -app.--style css. Расширение файла стилей.--skip-tests. Это позволяет избежать генерации файлов.spec.ts, которые используются для тестирования.
Установка Lit
Lit - это простая библиотека для создания быстрых и легких веб-компонентов.
Lit доступен через npm, давайте установим его как новую зависимость для текущего проекта.
npm install --save lit
Дополнительную информацию о Lit можно найти здесь.
Установка Web Components Polyfills
Есть несколько способов установить полифилы веб-компонентов. В этом случае мы установим его с помощью npm.
npm install --save @webcomponents/webcomponentsjs
Затем вы можете использовать webcomponents-loader.js, который позволяет загружать минимальный пакет полифиллов.
Обновляем Angular конфиг
Вы можете загрузить полифиллы с помощью тега <script> в файл index.html. Однако в Angular это можно сделать путем добавления новых конфигураций ресурсов и скриптов в файл angular.json следующим образом:
"architect": { "build": { ... "options": { ... "assets": [ "src/favicon.ico", "src/assets", { "glob": "{*loader.js,bundles/*.js}", "input": "node_modules/@webcomponents/webcomponentsjs", "output": "node_modules/@webcomponents/webcomponentsjs" } ], "scripts": [ "node_modules/@webcomponents/webcomponentsjs/webcomponents-loader.js" ] ... }, "configurations": { ... }, ... }, }
И входные, и выходные свойства должны относиться к корневому пути проекта. То же самое и с импортированным скриптом.
Создание веб-компонентов с использованием Lit
Настройка проекта завершена, и теперь пора создать наш первый веб-компонент с использованием Lit.
Давайте создадим папку src/web-components/card-user, а затем создадим два файла: card-user.ts и user.ts.
Файл user.ts будет определять общую модель, используемую реализацией веб-компонента и проектом Angular:
// user.ts export interface User { id: number; fullName: string; role: string; avatar?: string; }
Затем давайте напишем код для нашего веб-компонента в файле card-user.ts:
// card-user.ts import { LitElement, html, css } from 'lit'; import { property, customElement } from 'lit/decorators.js'; import { User } from './user'; @customElement('card-user') export class CardUser extends LitElement { static styles = css` :host { display: block; } .card { box-shadow: 0 10px 10px 0 rgba(0, 0, 0, 0.5); max-width: 160px; } .card-content { padding: 10px; } `; @property({ type: Object }) user?: User = { id: 0, fullName: 'Luis Aviles', role: 'Software Engineer', }; render() { if (this.user === undefined) { return ''; } return html` <div class="card"> <img width="160px" src=${this.user.avatar ? this.user.avatar : 'assets/images/avatar.png'} /> <div class="card-content"> <h4>${this.user.fullName}</h4> <p>${this.user.role}</p> <button @click=${this.handleEdit}>Edit</button> </div> </div> `; } private handleEdit() { this.dispatchEvent( new CustomEvent<User>('edit', { detail: this.user, }) ); } }
Класс CardUser определяет кастомный элемент как новый виджет для нашего проекта.
Декоратор
@customElementпозволяет определять компонент, используя для него имя:card-user.Поле
static stylesопределяет стили для компонента с литерала шаблонаcss.Декоратор
@property, который позволяет объявлять свойства кастомного элемента в удобном виде.Метод рендеринга возвращает содержимое HTML через литерал шаблона
html. Эта функция будет вызываться каждый раз при изменении свойства пользователя.
Вы можете узнать больше о том, как создать компонент с помощью Lit здесь.
Использование веб-компонентов в Angular
Импортируем веб-компонент
Перед использованием любого веб-компонента в Angular, нам необходимо импортировать его следующим образом:
// app.component.ts import { Component } from '@angular/core'; import '../web-components/card-user/card-user'; // <-- import the web component @Component({ selector: 'corp-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'], }) export class AppComponent {}
Используем кастомный элемент
Следующим шагом будет использование нового кастомного элемента <card-user> как части шаблона. В этом случае давайте использовать его в нашем файле app.component.html:
<card-user></card-user>
После сохранения этих изменений вы можете обнаружить ошибку в консоле (место, где вы запускаете ng serve).
Error: src/app/app.component.html:1:1 - error NG8001: 'card-user' is not a known element: 1. If 'card-user' is an Angular component, then verify that it is part of this module. 2. If 'card-user' is a Web Component, add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message. 1 <card-user></card-user> ~~~~~~~~~~~ src/app/app.component.ts:7:16 7 templateUrl: './app.component.html', ~~~~~~~~~~~~~~~~~~~~~~ Error occurs in the template of component AppComponent.
К счастью, предыдущее сообщение об ошибке достаточно понятно нам говорит, что нам нужно изменить файл app.module.ts.
// app.module.ts import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; @NgModule({ declarations: [AppComponent], imports: [BrowserModule, AppRoutingModule], providers: [], bootstrap: [AppComponent], schemas: [CUSTOM_ELEMENTS_SCHEMA], }) export class AppModule {}
Важная часть здесь - импортировать CUSTOM_ELEMENTS_SCHEMA из пакета @angular/core, а затем использовать его в свойстве schemas. Вы можете найти больше информации об этом здесь.
Сохраните изменения еще раз, и волшебство произойдет :-)

Использование Property Binding
Удобно знать, что мы можем не только импортировать внешние компоненты в наше приложение, но и использовать привязку свойств.
Для этого определим свойство user в файле app.component.ts:
// app.component.ts export class AppComponent { user: User = { id: 2, fullName: 'Luis', role: 'Software Engineer', avatar: 'https://luixaviles.com/images/avatar@2x.png', }; }
Затем мы можем обновить связанный шаблон и использовать квадратные скобки для привязки свойства:
<card-user [user]="user"></card-user>
Более того, вы можете создать массив пользователей, чтобы иметь возможность отображать их, используя другие полезные функции из нашего любимого фреймворка, например, структурная директива *ngFor.
<card-user *ngFor="let user of users" [user]="user"></card-user>
Использование Event Binding
Пришло время слушать действия пользователя и реагировать на них через привязку событий в Angular. Как вы видели ранее, компонент corp-user может отправлять событие edit после нажатия кнопки «Изменить».
На этот раз воспользуемся круглыми скобками для привязки метода:
<card-user *ngFor="let user of users" [user]="user" (edit)="edit($event)"></card-user>
Наконец, давайте создадим метод редактирования в нашем компоненте.
// app.component.ts //... export class AppComponent { //... edit(event: Event) { const user = (event as CustomEvent<User>).detail; console.log('Edit user', user); } }
Метод edit получает обобщенный Event. Хотя кастомный элемент отправил объект User через CustomEvent. Затем мы можем использовать синтаксический оператор as из TypeScript и получить к нему доступ с помощью свойства detail. На следующем снимке экрана показаны результаты в консоли браузера.

Исходный код проекта
Вы можете найти полный код в этом репозитории GitHub: angular-lit-web-components. Не забудьте поставить звездочку ⭐️ и поэкспериментировать с кодом.
