Play FrameworkにAngular Elementsを導入してみた。
- はじめに
- 本記事で行うこと
- Angular Elementsを導入しようと思った理由
- Angular Elementsのインストール
- Angular Elements の実装
- 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を実装する方法