Play FrameworkにAngular Elementsを導入してみた。

takahiko tominaga
nextbeat-engineering
12 min readDec 25, 2020
  1. はじめに
  2. 本記事で行うこと
  3. Angular Elementsを導入しようと思った理由
  4. Angular Elementsのインストール
  5. Angular Elements の実装
  6. Play Frameworkで読み込み設定

1. はじめに

今回は、前回のScala/Play Frameworkの認証機能とAngularの導入をしたアプリに、Angular Elementsの導入を行いましたので、そのまとめになります。
下記が前回のブログ記事になりますので、よければこちらも読んでいただけると嬉しいです。

2. 本記事で行うこと

本記事では、Angular Elementsの導入を行い、Angularで作成したコンポーネントを、Play FrameworkのTwirlに埋め込んで使用できるようにしていきます。

3. Angular Elementsを導入しようと思った理由

前回のブログでは、フロントを全てAngularで作成していました。導入した時はAngularを導入したら、フロントは全部Angularで作るものだと思って実装を進めていました。

しかし、実装を進める中で、Angularに関して調べていると、Angularで作ったコンポーネントを、Angular Elementsの機能で、Custom Elements(後程で説明)としてパッケージ化を行う機能があると知りました。

パッケージ化を行うことで、HTMLのDOMとしてAngularの外部でも扱えるようになります。

つまり、Angulat Elementsを使えば、バックエンド側のビューにもAngularのコンポーネントを埋め込めるようになり、ページの遷移や認証の検証をバックエンドで行いつつ、画面はAngularで作成できるということがわかったので、導入しようと思いました。

4. Angular Elementsのインストール

プロジェクトディレクトリに移動して、以下のコマンドを実行します。

$ cd ui
$ ng add @angular/elements

上記コマンド実行後、document-register-element.jsなどのAngular Elementsに必要なポリフィルや依存関係がプロジェクトに追加されます。

5. Angular Elements の実装

Angularを表示させるための処理としてbootstrapの設定を行う必要があります。
この設定が、通常のAngularコンポーネントの場合とAngular Elementsのコンポーネントの場合では異なるので、下記で説明を行います。

5–1. 通常のAngularコンポーネントの場合

Angularのドキュメントに記載されているように、作成したコンポーネントをブラウザDOMに挿入するためには、@NgModuleのbootstrap配列にコンポーネントを追加しなければなりません。
Angularのindex.htmlに、bootstrap配列に追加していないコンポーネントを記載しても、ブラウザ上に表示されないのはこのためです。

bootstrap配列に追加されたコンポーネントが、ルートコンポーネントとなり、ルートコンポーネントを根元として作成したコンポーネントを 、ブラウザDOMに挿入していきます。
なので、本来であれば作成したコンポーネントを、ブラウザDOMに挿入したいのであれば、bootstrap配列に追加するか、もしくはbootstrap配列に追加したルートコンポーネントを介して表示させる必要があります。

5–2. Angular Elementsの場合

Angular Elementsは、自分自身でブートストラップを行います。
Custom ElementsとしてDOMに追加されると自動的にブートストラップし、取り除かれると自分自身で破棄します。
つまり、ここでは通常のAngularで行うブートストラップの処理は必要ありません。[6]

Angular Elementsは上記のような処理を行います。

ただAngular Elementsでは、Custom Elements化したいコンポーネント(本記事では「LoginComponent」)と、このコンポーネントをCustom Elements化するための設定を行うコンポーネント(本記事では「ElementComponent」)2つの設定を行う必要があります。

なぜなら、Angular Elementsは、自分自身でブートストラップの処理を行うので別途設定を行う必要はないが、Custom Elementsの設定をしているコンポーネントは、ブートストラップの処理を行う必要があるからです。

以上の理由から、下記のようにbootstrap配列にしっかりと追加しておきます。

@NgModule({
....
bootstrap: [ElementComponent]
})

それでは、まずCustom Elementsの設定を行い、ブートストラップの設定を行わなくても、ブラウザ上に表示されるかを確認していきます。

5–3. Custom Elementsの設定

Custom Elementsの設定を行うことによって、設定したコンポーネントは自分自身でブートストラップ処理ができるようなります。

@angular/elementsモジュールのcreateCustomElementメソッドと、Custom Elementsで用意されているcustomElements.defineメソッドを使って、ブートストラップの処理を書きます。

customElements.defineメソッドの最初の引数にブラウザDOMに埋め込む際のタグ名を設定します。
このタグ名は、コンポーネントで設定するselector名と同じにする必要があります。

2番目の引数にCustom Elements化したいコンポーネントをcreateCustomElementメソッドで設定します。

これで、設定したコンポーネントをCustom Elementsとして使用することができます。

5–4. Angular Elementsのビルド

続いて、Angular Elementsとして実装したものを、Play Frameworkで読み込むためのjsファイルに、ビルドする設定を行っていきます。

package.jsonに既に登録されている以下のビルド用のコマンドを実行します。

“build:dev”: “ng build — progress — output-path ../public”

実行が完了すると、distディレクトリが新規に作成されて、その中にビルドされたファイルが出力されています。

runtime.js、polyfills.js、main.jsの3つのファイルが生成されているのが確認できると思います。

Play Framework側で、作成したCustom Elementsを使用したい場合、この3つのファイルを下記のように全て読み込まないと、使用することができません。

<script src="runtime.js"></script>
<script src="polyfills.js"></script>
<script src="main.js"></script>

3つのjsファイルを読み込むのは冗長なので、複数のjsファイルを単一のjsファイルとしてビルドし、1つのファイルを読み込むだけでCustom Elementsを使用できるように設定していきます。参考元[2]

そのため、ビルドコマンドが記載されている、package.jsonに下記コマンドを追加します。
下記コマンドは、生成されるruntime.js, polyfills.js, main.jsの3ファイルを、ng-element.jsというファイルにまとめて生成するように設定しています。
最後の>以降でどこに生成するかを指定できます。

今回はTwirlで使用できるように、デフォルトであるPlayのpublicディレクトリ以下に保存するようにします。

"build:elements": "ng build --prod --output-hashing=none && cat dist/ui/{runtime,polyfills,main}.js > ../public/javascripts/ng-element.js"

実際にコマンドを実行してみます。
コマンドを実行すると、下記画像のようになり、先ほどruntime.js, polyfills.js, main.jsの3ファイルが生成されていたdistディレクトリにng-element.jsというファイルが生成されます。

これでAngular Elementsのビルドは完了です。
他サイトで使用できるか確認する前に、Angularのプロジェクト内でブートストラップ配列に追加せずにコンポーネントが使用できるか確認してみます。

Angularのindex.htmlにCustom Elements化した、LoginComponentをセットしてブラウザで確認してみます。

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Play-Scala-App</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<login-form></login-form>
</body>
</html>

ブラウザ(localhost:4200)を開いて確認すると下記画像のように、表示されているのが確認できました。
これで、Angular Elementsは、自分自身でブートストラップを行うということが確認できましたので、Custom Elementsの設定をしたコンポーネントを、Play Frameworkで使用できるかを確認していきます。

6. Play Frameworkで読み込み設定

Play FrameworkのTwirlでbody要素内に、先ほどビルドしたAngularElementsを、scriptタグで読み込んでいます。
※Play側のpublicディレクトリ内に保存させたものを読み込んでいます。

これでPlay Framework側でAngularElementsのコンポーネントを使用できるようになりました。

Twirlにコンポーネントをセットしたので、ブラウザ(localhost:9000)を開いて確認してみましょう。

無事に表示されているのが、確認できました。これでAngular Elements導入の設定は完了です。

まとめ

Angular Elementsの導入自体は、思っていたよりも複雑ではありませんでした。
どちらかというと、Angularのbootstrapなどの@NgModuleの方が難しかったです。Custom Elementsが自動的にブートストラップするということだったので、Custom Elementsの処理を行うコンポーネントもbootstrap配列に追加する必要はないと思っていました。最初はその設定をしなかったため、上手く動かず苦戦してしまいました。
他にも色々とエラーがおきましたが、今回の導入で少しAngularの知識が広がったので、よかったです。

次は、今やっているAngularのControlValueAccessorに関して、まとめていけたらなと考えています。

最後まで読んでいただき、ありがとうございました!

参考文献

[1] ルートモジュールによるアプリケーションの起動
[2] Building Custom Elements / Web Components with Angular 6
[3] [Angular] ブートストラップ — @NgModuleとは何か (3)
[4] Angular Elements概要
[5] Using custom elements
[6]Angular Elementsことはじめ — Custom Elementsを実装する方法

--

--