Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Angular Elements を用いた Web Components 製 UIライブラリの提供

Angular Elements を用いた Web Components 製 UIライブラリの提供

GDG DevFest 2020
mini ng-japan 2020

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)
https://stackoverflow.com/questions/52797841/how-to-call-a-method-on-a-angular-web-component-custom-element
- Call Angular web component method (CustomElement)
https://stackoverflow.com/questions/62804721/call-angular-web-component-method-customelement

Masashi Kondo

October 17, 2020
Tweet

Other Decks in Programming

Transcript

  1. Agenda Angular Elements を⽤いた Web Components 製 UIライブラリ開発について、 ⾃社事例を元に説明します。 1.

    サービス紹介 2. Angular Elements 3. Safie Web Components 4. 開発時の課題と対策
  2. 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); } }
  3. 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(); }
  4. Angular Elements フレームワークに依存しないコ ンポーネントを簡単に作成でき る。 Angular Elements <body> <a-component text="GDGdevFest"></a-component>

    <script src="https://wc.download.url/"></script> <script> const aComponent = document.querySelector('a-component'); aComponent.type = "normal"; aComponent.addEventLister('change', () => { console.log('changed'); }) </script> </body>
  5. ViewEncapsulation .ShadowDom そのまま Angular Elements を Custom Elements にする と

    Scoped CSS が適応されま せん。 Scoped CSS を使うには Components で encapsulation を設定。 開発時の課題と対策 ••••• import { Component, ViewEncapsulation, // } from '@angular/core'; @Component({ template: ' <span class="light">Light Component</span> ', }) export class LightDomComponent {} @Component({ template: ' <span class="shadow">Shadow Component</span> ', encapsulation: ViewEncapsulation.ShadowDom, // }) export class ShadowDomComponent {}
  6. ViewEncapsulation .ShadowDom Web Components は ShadowDom を使って Scoped CSS を実現する。

    createCustomElement する Components で ShadowDom を指定する。 開発時の課題と対策 ••••• DEMO
  7. 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; } } // <a-component text="sample" type="special"></a-component> //
  8. Attribute Mapping 今回は <video> のように 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; } }
  9. Attribute Mapping 関数を定義する術が無いので、 別 Components に play() と pause() を定義。

    Custom Elements から instance として Components を取得して実⾏ する。 開発時の課題と対策 •••••
  10. 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'); } }
  11. Attribute Mapping 出⼒した Custom Element か ら instance を取得。 instance

    に対して play() 等 の関数を呼ぶ。 開発時の課題と対策 ••••• <body> <outer-component></outer-component> <script src="https://wc.download.url/"></script> <script> const outerComponent = document.querySelector('outer-component'); const innerComponent = outerComponent.instance; function play() { innerComponent.play(); // call play } function pause() { innerComponent.pause(); // call pause } </script> </body>
  12. Error Handling 利⽤者は Web Components で発⽣したエラーを検知するこ とが出来ない。 <video> の様に onerror

    を実 装する必要がある。 開発時の課題と対策 ••••• import { Component } from '@angular/core'; @Component({ template: ' <input type="button" value="throw" (click)="onClick()" > ', }) export class AComponent { onClick() { throw Error('throw Error!!!'); // } }
  13. Error Handling @Output で Custom Event と して実装するのが良いが、前述 の件があったので instance

    で EventEmitter を使い実装し た。 開発時の課題と対策 ••••• <!-- Safie Web Components --> <body> <safie-streaming-player></safie-streaming-player> <script src="https://wc.download.url/"></script> <script> const safieStreamingPlayerElement = document.querySelector('safie-streaming-player'); const safieStreamingPlayer = safieStreamingPlayerElement.instance; safieStreamingPlayer.on('error', (error) => { console.error(error); }); </script> </body>
  14. Composable Definition Pattern 今後の課題として、定義する Components が増えると Module (Component) の共 通部分への依存が増えてくる。

    これを効率化し、オーバーヘッ ドを減らしたい。 開発時の課題と対策 •••••
  15. 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); } }
  16. Composable Definition Pattern ⼀つの Web Components と して出⼒することで、 Component (Module)

    の他 に bootstrapping や <script> のロードに掛かる共 通のオーバーヘッドも削減でき る。 開発時の課題と対策 •••••
  17. おわりに Angular Roadmap では今後Ivyでの最適化や、Optional ngModule などが予定さ れており、Angular Elements で出⼒されるWeb Componentsもその恩恵を受けれ

    ることを期待しています。 Web Components の開発経験はほぼ0でしたが、 Angular Elements のお陰で想 定より簡単でした。是⾮使って頂ければと思います。
  18. 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)