Slide 1

Slide 1 text

Angular Elements を⽤いた Web Components 製 UIライブラリの提供 Kondo Masashi GDG DevFest 2020 mini ng-japan 2020

Slide 2

Slide 2 text

About Me

Slide 3

Slide 3 text

Agenda Angular Elements を⽤いた Web Components 製 UIライブラリ開発について、 ⾃社事例を元に説明します。 1. サービス紹介 2. Angular Elements 3. Safie Web Components 4. 開発時の課題と対策

Slide 4

Slide 4 text

●●●● サービス紹介

Slide 5

Slide 5 text

クラウド録画サービス︓Safie ネットワークカメラの映像をクラウドに保存。 いつでもライブや録画映像を視聴。 Web の映像ビューアーは Angular 10 で開発。 サービス紹介

Slide 6

Slide 6 text

Safie Viewer DEMO

Slide 7

Slide 7 text

映像視聴 UI ライブラリ ユーザーが⾃分のビジネスとコラボして映像を使えるようにする。 そのためには⾃社サイト以外でも録画映像を⾒れるようにしたい。 録画映像を扱う UI ライブラリとしてのニーズが出てきた。 ライブラリは標準規格である Web Components で提供。 Angular Elements を⽤いて実装した。 サービス紹介

Slide 8

Slide 8 text

●●●● Angular Elements

Slide 9

Slide 9 text

Angular Elements Angular の Components を Web Components (Custom Elements) に変換し て出⼒。 Angular Elements import { NgModule, Injector } from '@angular/core'; import { createCustomElement } from '@angular/elements'; import { AComponent } from './components/a.component'; @NgModule({ declarations: [ AComponent, ], }) export class AppModule { constructor(private injector: Injector) {} ngDoBootstrap() { const AComponentElement = createCustomElement( AComponent, { injector: this.injector } ); customElements.define('a-component', AComponentElement); } }

Slide 10

Slide 10 text

Angular Elements @Input は Attribute に @Output は Custom Event に マッピングされる。 ライフサイクルフックも可能。 Angular Elements import { Component, Input } from '@angular/core'; @Component({ template: 'A_Component', }) export class AComponent { private _type = 'normal'; @Input() text = ''; @Input() set type(value: string) { this._type = value; } get type() { return this._type; } @Output() change = new EventEmitter(); }

Slide 11

Slide 11 text

Angular Elements フレームワークに依存しないコ ンポーネントを簡単に作成でき る。 Angular Elements const aComponent = document.querySelector('a-component'); aComponent.type = "normal"; aComponent.addEventLister('change', () => { console.log('changed'); })

Slide 12

Slide 12 text

●●●● Safie Web Components

Slide 13

Slide 13 text

Safie Web Components フレームワークに依存しない Web Components 製のUIライブラリ。 映像視聴に特化したUIを提供。 Safie Web Components

Slide 14

Slide 14 text

Safie Web Components DEMO

Slide 15

Slide 15 text

●●●● 開発時の課題と対策

Slide 16

Slide 16 text

開発時の課題と対策 Angular Elements を⽤いた開発で⼯夫したこと。 特に通常の Angular 開発で普段気にしないことについて説明する。 1. ViewEncapsulation.ShadowDom 2. Attribute Mapping (@input) 3. Error Handling 4. Composable Definition Pattern 5. Lazy Loading (Component) 開発時の課題と対策

Slide 17

Slide 17 text

ViewEncapsulation.ShadowDom 開発時の課題と対策

Slide 18

Slide 18 text

ViewEncapsulation .ShadowDom そのまま Angular Elements を Custom Elements にする と Scoped CSS が適応されま せん。 Scoped CSS を使うには Components で encapsulation を設定。 開発時の課題と対策 ●●●●● import { Component, ViewEncapsulation, // } from '@angular/core'; @Component({ template: ' Light Component ', }) export class LightDomComponent {} @Component({ template: ' Shadow Component ', encapsulation: ViewEncapsulation.ShadowDom, // }) export class ShadowDomComponent {}

Slide 19

Slide 19 text

ViewEncapsulation .ShadowDom Web Components は ShadowDom を使って Scoped CSS を実現する。 createCustomElement する Components で ShadowDom を指定する。 開発時の課題と対策 ●●●●● DEMO

Slide 20

Slide 20 text

Attribute Mapping 開発時の課題と対策 ●●●●●

Slide 21

Slide 21 text

Attribute Mapping Angular Elements では @input() が付与されたプロパ ティを Custom Element の属 性として扱う。 開発時の課題と対策 ●●●●● import { Component, Input } from '@angular/core'; @Component({ template: 'Component_A', }) export class AComponent { private _type = 'normal'; @Input() text = ''; @Input() set type(value: string) { this._type = value; } get type() { return this._type; } } // //

Slide 22

Slide 22 text

Attribute Mapping 今回は のように play() 、 pause() などのメソ ッドを定義する必要があった。 Angular Elements には function を定義する術が無 い。 開発時の課題と対策 ●●●●● import { Component, Input } from '@angular/core'; @Component({ template: 'Component_A', }) export class AComponent { _timestamp = new Date().getTime(); @Input() mute = false; // 参照可能 play() { // is not a function console.log('call play'); } @Input() pause() { // is not a function console.log('call pause'); } @Input() get timestamp() { // 参照可能 return this._timestamp; } }

Slide 23

Slide 23 text

Attribute Mapping 関数を定義する術が無いので、 別 Components に play() と pause() を定義。 Custom Elements から instance として Components を取得して実⾏ する。 開発時の課題と対策 ●●●●●

Slide 24

Slide 24 text

Attribute Mapping Custom Element になる外側 の Component に instance を @Input の Getter で定義。 内側の Component に関数を 定義する。 開発時の課題と対策 ●●●●● import { Component, Input } from '@angular/core'; @Component({ template: 'OuterComponent', }) export class OuterComponent { innerComponent = new InnerComponent(); @Input() get instance() { // return this.innerComponent; } } @Component({ template: 'InnerComponent', }) export class InnerComponent { play() { // console.log('call play'); } pause() { // console.log('call pause'); } }

Slide 25

Slide 25 text

Attribute Mapping 出⼒した Custom Element か ら instance を取得。 instance に対して play() 等 の関数を呼ぶ。 開発時の課題と対策 ●●●●● const outerComponent = document.querySelector('outer-component'); const innerComponent = outerComponent.instance; function play() { innerComponent.play(); // call play } function pause() { innerComponent.pause(); // call pause }

Slide 26

Slide 26 text

Error Handling 開発時の課題と対策 ●●●●●

Slide 27

Slide 27 text

Error Handling 利⽤者は Web Components で発⽣したエラーを検知するこ とが出来ない。 の様に onerror を実 装する必要がある。 開発時の課題と対策 ●●●●● import { Component } from '@angular/core'; @Component({ template: ' ', }) export class AComponent { onClick() { throw Error('throw Error!!!'); // } }

Slide 28

Slide 28 text

Error Handling @Output で Custom Event と して実装するのが良いが、前述 の件があったので instance で EventEmitter を使い実装し た。 開発時の課題と対策 ●●●●● const safieStreamingPlayerElement = document.querySelector('safie-streaming-player'); const safieStreamingPlayer = safieStreamingPlayerElement.instance; safieStreamingPlayer.on('error', (error) => { console.error(error); });

Slide 29

Slide 29 text

Composable Definition Pattern 開発時の課題と対策 ●●●●●

Slide 30

Slide 30 text

Composable Definition Pattern 今後の課題として、定義する Components が増えると Module (Component) の共 通部分への依存が増えてくる。 これを効率化し、オーバーヘッ ドを減らしたい。 開発時の課題と対策 ●●●●●

Slide 31

Slide 31 text

Composable Definition Pattern Angular Elements は複数の Component (Module) を⼀ つの Web Components とし て定義できる。 開発時の課題と対策 ●●●●● import { NgModule, Injector } from '@angular/core'; import { createCustomElement } from '@angular/elements'; import { AComponent } from './components/a.component'; import { BComponent } from './components/b.component'; @NgModule({ declarations: [ AComponent, BComponent ], }) export class AppModule { constructor(private injector: Injector) {} ngDoBootstrap() { const AComponentElement = createCustomElement( AComponent, { injector: this.injector } ); const BComponentElement = createCustomElement( BComponent, { injector: this.injector } ); customElements.define('a-component', AComponentElement); customElements.define('b-component', BComponentElement); } }

Slide 32

Slide 32 text

Composable Definition Pattern ⼀つの Web Components と して出⼒することで、 Component (Module) の他 に bootstrapping や のロードに掛かる共 通のオーバーヘッドも削減でき る。 開発時の課題と対策 ●●●●●

Slide 33

Slide 33 text

Lazy Loading 開発時の課題と対策 ●●●●●

Slide 34

Slide 34 text

Lazy Loading 更に効率を上げるため、Lazy Loading を活⽤する。 共通部分をダウンロードし、必 要な Components をロードす ることができる。 開発時の課題と対策 ●●●●●

Slide 35

Slide 35 text

Lazy Loading Angular Elements での Lazy Loading デモ 開発時の課題と対策 ●●●●● DEMO

Slide 36

Slide 36 text

おわりに Angular Roadmap では今後Ivyでの最適化や、Optional ngModule などが予定さ れており、Angular Elements で出⼒されるWeb Componentsもその恩恵を受けれ ることを期待しています。 Web Components の開発経験はほぼ0でしたが、 Angular Elements のお陰で想 定より簡単でした。是⾮使って頂ければと思います。

Slide 37

Slide 37 text

Finish! Enjoy GDG DevFest and ng-japan

Slide 38

Slide 38 text

Github https://github.com/MssKnd/angular-elements-demo

Slide 39

Slide 39 text

Reference Angular.io Angular elements overview Angular Roadmap blog.lacolaco.net Angular Elementsの現在地 (2020 Summer) Angular Elements: Composable Definition Pattern open-wc.org Web Component Libraries stackoverflow.com How to call a method on a Angular Web Component (Custom Element) Call Angular web component method (CustomElement)

Slide 40

Slide 40 text

This slides powered by Marpit (VSCode Plugin)