Как интегрировать веб-компоненты с помощью Lit в Angular

от автора

Эта статья — перевод оригинальной статьи 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. Не забудьте поставить звездочку ⭐️ и поэкспериментировать с кодом.


ссылка на оригинал статьи https://habr.com/ru/articles/583924/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *