Quantcast
Channel: Angular Advent Calendarの記事 - Qiita
Viewing all articles
Browse latest Browse all 25

AngularElementsでAMP htmlを動的にロードする

$
0
0

本記事ではAngularElementsを使ってAMP htmlをロードするという試みをします。
AMPは、ページを高速に表示してSEOがよくなるやつですね。1

Angular Elementsについて

ブートストラップ以下のツリーを通さず直接DOMにAngularComponentを追加できるようになるです。

ユースケースは1年前の記事でごちゃごちゃ書きましたが、相変わらず2つパラレルに走っています。

Angularアプリ内でElementsを使う方法

公式ドキュメントのサンプルが現在このユースケースです。Angularアプリ内で動的コンポーネントをどうやって実現するかという方向です。サンプルでは、AngularComponentでそのままやる方法と、customElementを用いる方法の2つが併記されています。

「サンプル: ポップアップサービス」

単発のcustomElementsとしてビルドして配布し、他のアプリケーションから使用する方法

この記事が非常にクオリティ高いです。
customElementのブートストラップや他のelementとの外部インターフェースについて理解が深まります。結論としてはivy待ちですね。

「Angular Elementsことはじめ – Custom Elementsを実装する方法」

また、公式ドキュメントですが、配布用のユースケースを載せてよというイシューが上がっているため、そのうち出てきそうです。

AMP htmlをAngularElementsでロードする

  • AngularElementsを使います。
  • サーバからAMP htmlを読み込み、elementsのiframeにセットします。

実装

公式ドキュメントのサンプルをベースに進めていきます。

AMP表示用のcomponentを用意する

いつものようにcomponentを作ります。iframeタグを用意してsrcdoc属性にXHRで取得したAMPhtmlソースをセットしようとしています。このcomponentは後にcusutomElement化します。そうすると@InputがHTMLElementのproperty属性になります。

amp-display.component.ts
@Component({
  selector: 'app-amp-display',
  template: `
    <iframe [srcdoc]="safeMsg"></iframe>
    <button (click)="closed.next()">&#x2716;</button>
  `
})
export class AmpDisplayComponent {
  safeMsg: SafeHtml;

  // customElement化した時にproperty属性になります。
  @Input()
  set message(message: string) {
    this.safeMsg = this.sanitizer.bypassSecurityTrustHtml(message);
  }

  @Output()
  closed = new EventEmitter();

  constructor(private sanitizer: DomSanitizer) {}
}

customElement化する

ここは公式ドキュメントのサンプルそのままです。ソースコメントにいたっては完全にコピペです。
AppComponentのコンストラクタ内で先ほどのcomponentをNgElementという変換器にかけた上でwindow.customElementsに登録します。これでもう標準のカスタム要素になりました。DOM APIから<app-amp-display>で操作可能です。逆にAngular内で扱おうとすると、例えばtemplateに<app-amp-display>をセットしても様々な困難がやってきます。

app.component.ts
@Component({
  selector: 'app-root',
  template: `
    <button (click)="ampDisplay.showAsElement()">Show as element</button>
  `
})
export class AppComponent {
  constructor(injector: Injector, public ampDisplay: AmpDisplayService) {
    // Convert `PopupComponent` to a custom element.
    const AmpDisplayElement = createCustomElement(AmpDisplayComponent, {
      injector
    });
    // Register the custom element with the browser.
    customElements.define('amp-display-element', AmpDisplayElement);
  }
}

AMPを読み込む

serviceを起こして、responseType: 'text'でサーバからAMPソースを読み込むようにします。AMPソースは、URLを叩けば普通に表示できる<html>タグ以下のフルhtmlドキュメントです。

@Injectable()
export class AmpDisplayService {
  private ampUrl = 'http://localhost:8080/amp';

  constructor(
    private http: HttpClient
  ) {}

  private getContent() {
    return this.http.get(this.ampUrl, { responseType: 'text' });
  }
}

Angularアプリ内からcustomElementを呼び出す

serviceにcustomElementをDOMに付与するメソッドを追加します。すでにwindowには登録済みなので、document.createElement()で作成できるようになっています。@Input() messageがHTMLElementのproperty属性として変換されているため、取得したAMPソースを渡してやります。

@Injectable()
export class AmpDisplayService {
  private ampUrl = 'http://localhost:8080/amp';

  constructor(
    private injector: Injector,
    private http: HttpClient
  ) {}

  // This uses the new custom-element method to add the popup to the DOM.
  showAsElement() {
    // Create element
    const ampDisplayEl: NgElement &
      WithProperties<AmpDisplayComponent> = document.createElement(
      'amp-display-element'
    ) as any;

    // Listen to the close event
    ampDisplayEl.addEventListener('closed', () =>
      document.body.removeChild(ampDisplayEl)
    );

    this.getContent().subscribe(text => {
      // Set the message
      ampDisplayEl.message = text;
      // Add to the DOM
      document.body.appendChild(ampDisplayEl);
    });
  }
}

APMコンテンツを用意する

以下のAMPチュートリアルを参考にHTMLドキュメントを用意しました。expressサーバから供給します。

https://www.ampproject.org/ja/docs/getting_started/create/basic_markup

結果

ボタンを押すと、AMPソースをサーバーから取得して、トーストに表示しています。グレーの背景がAMPソースです。

トリム.mov.gif

リポジトリ

今回のコードはこちらにアップしています。

https://github.com/studioTeaTwo/elements-amp

ほんとうにやりたかったこと

正直、iframeは逃げました。ほんとうにやりたかったことは、iframeの代わりにshadowDOMを使ってAMPを表示する2という話がありました。そこまで来るとAngularElementsを使ってcustomElementにする意味もあるというもので。しかし、shadowRootに<html>タグ以下をそのまま追加すればいいってものでもなく、AMP projectで用意されているwindow.AMP.attachShadowDoc()という謎モジュールの解明で時間切れとなりました。reactのサンプル実装があるので、ご興味ある方はトライしてみてください。

「AMP を埋め込んでデータソースとして使用する」

デモサイト3を見るとおおよそやってることはわかるかと思います。今回やろうとしていた特定領域に出力先をコントールするのではなくドキュメント全体に適用するような手法で、実はそれならAngularでもwindow.AMP.attachShadowDoc()を使ってできました。機会を見てまた記事にするかもしれません。

明日は@Czernyさんです。


  1. https://www.ampproject.org/ja/learn/overview/ 

  2. https://html5experts.jp/shumpei-shiraishi/24795/ 

  3. https://choumx.github.io/amp-pwa AMPhtmlから<style><body>タグだけを取り出してshadowRootに適用している感じです。<meta><script>はホストのreactドキュメントに適用しているような動きに見えます。shadow-v0.jsというのがOSS化されてないと思うので出力結果からの憶測です。 


Viewing all articles
Browse latest Browse all 25

Latest Images

Trending Articles