利用するフレームワークの紹介
今回、利用するフレームワークとしてAngularJS(1.x)とKnockoutJSを使う場合を紹介します。ただし、今回はMVC構造やフレームワークの使い方を紹介するものではありません。MVC構造などについてや、これらのフレームワークについてより詳しく知りたい場合には、こちらの記事を読んでもらえばと思いますが、これらのフレームワークはすでに多く使われていて、しかもHTMLテンプレートが書きやすいフレームワークです。
そして、JavaScriptについて深い知識がなくても使えるので、サーバサイドで記述していたHTMLをフロント側に移行するような用途にも向いているはずです。ただし、注意点としてAngularJSは最近2.0がリリースされています。今回、利用するバージョンは1.xですが、2.xからは前提としている知識や用途も大きく変わってしまっており、まだ、ドキュメント等やノウハウも豊富にそろっているとはいえないため、フロントエンドエンジニア、もしくはそれと同様の知識を持っていないと現段階では利用が難しいと思います。よって、本稿では1.xを利用しています。
これらのフレームワーク以外でも特に問題はありませんが、サーバサイドエンジニアとクライアントサイドエンジニアの双方がわかりやすく、また、HTMLだけがわかる方も編集できることが大切です。
JavaScriptのフレームワークを選択するとなると、多くの方が考えてしまいがちなことは、誰が利用するかよりも、
- 最もトレンドなフレームワークは何か?
- 今後、普及しそうなものは何か?
などを追求してしまうことです。
現在のJavaScriptフレームワークでは、サーバサイドでのHTMLテンプレートの高度化と同じように、クライアントサイドでもHTMLテンプレートの高度化が進んでいます。
これは、最近のJavaScriptのフレームワークがネイティブアプリにくらべ、欠点である速度面などで問題が生じないように施策を施したり、フロントエンドエンジニアがより高度なWebアプリを作るための機能強化にトレンドが変わりつつあるためです。少し前までは、どのようにわかりやすくDOM構造を作るかに主眼が置かれていたので、そのような点も考慮したフレームワークとして筆者が推奨するフレームワークとして選択したのが、AngularJS(1.x)とKnockoutJSです。
今回紹介したフレームワークから別のフレームワークへ移行する場合でも、すでにクライアントサイド側の役割が明確に切り分けられていれば、移行もしやすいはずです。
サーバサイド側の仕組みをなるべく変えない方法
では、実際にクライアント側でHTMLテンプレートを使うための具体例を紹介します。
まずは、サーバサイド側でテンプレートを管理されていたところからの移行となりますので、できるだけ、現行で動作している仕組みと同じような流れを維持したまま、HTMLテンプレート部分にだけ手を加えていく方法を紹介します。
十分な経験をもったフロントエンドエンジニアがチーム内にいない場合には、通信面の変更も含めたREST APIとして稼働させるには、少々、影響範囲が大きすぎると懸念されるためです。
つまり、サーバサイドはできるだけ同じように図2のようにデータとHTMLテンプレートを1つのデータとして作り、まずは、このような方法で作成することから始めれば良いと思います。
AngularJSを使った場合の例
概念だけでは少々、わかりにくいので、実際のコードの例(AngularJSを利用した場合)をリスト1(画面キャプチャーは図3)に示します。AngularJSの使い方について詳細を理解をする必要はありません。ここで記述されているコードの内容を知りたい場合には筆者の記事AngularJSでMVCプログラミングをはじめようなどを参照してください。
<!DOCTYPE html> <html lang="ja" ng-app="main"> <!-- (1)ng-appを指定する --> <head> <meta charset="UTF-8"> <title>STEP1 - AngularJS</title> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.js"></script> <!--(2)実際のJavaScriptコード --> <script type="text/javascript" src="script.js"></script> <!--(3)サーバサイド側の処理 --> <script><?php $json = array( user => array( id => 1, name => "日本 太郎", ) ); printf("var json='%s';", json_encode($json, JSON_UNESCAPED_UNICODE)); ?></script> </head> <body ng-controller="PageController"> <!--(4)ng-controllerを指定する --> <table border="1"> <tr> <td>ID</td> <!--(5)AngularJSでの変数の指定方法 --> <td>{{user.id}}</td> </tr> <tr> <td>名前</td> <!--(6)AngularJSでの入力要素での値の指定方法 --> <td><input type="text" ng-model="user.name"></td> </tr> </table> </body> </html>
(1)のようにAngularJSで必要な指定であるng-appがありますが、現時点では、このような指定が必要であるとの程度の理解で問題ありません。続いて、(2)では実際に処理するためのJavaScriptコードを別のファイルへと分離して作成しています。内容は後述しますが、JavaScriptによるロジックは、HTMLファイルから完全に分離することが大切です。jQueryを使っていた場合には、id属性やclass属性などを用いて非常にDOM構造と密接になりがちであり、分離すると直感的にわかりにくくなるということがありましたが、クライアント側HTMLテンプレートを使えば、サーバサイドと同様な形で分離がしやくなることは理解できるかと思います。
(3)ではサーバ側で生成した値をHTML内に値を記述してJavaScript側に値を渡します。今回は説明上、PHPでの簡単な処理を記述していますが、実際にはもう少し複雑な実装になるかもしれません。その場合には、この部分をサーバサイド側のテンプレートエンジンで記述をしてもよいかもしれません。
そして、(4)も(1)と同様にAngularJSで必要な指定ですので、まずは指定が必要であるという理解で問題ありません。
(5)と(6)がクライアント側での値を差し込む指定です。(3)で出力した値をラベルとして表示する場合とINPUT要素のような入力要素の値として設定する場合には多少指定方法が異なります。
このHTMLだけを見ればサーバサイドで作成しているHTMLとあまり変わらない形式であることがわかると思います。
このように、実際の処理の流れは異なっていても、サーバサイドのテンプレートエンジンで処理をしていたのと同じようなコードで記述が可能です。実際には、値の設定をしているJavaScript部分が必要ですが、そのJavaScriptコードはリスト2のようになります。
var module = angular.module('main',[]); //(1)ng-appで指定した部分に相当 function PageController($scope){ //(2)// テンプレート内で使用できる変数として設定 var data = JSON.parse(json); for(var name in data){ $scope[name] = data[name]; } }; //(3)ng-controllerで指定した部分に相当 module.controller('PageController',['$scope',PageController]);
このコードは、JSONで定義されたデータをすべてHTMLテンプレート内で利用できるようにしているコードです。そのための処理を行っているのが、(2)の部分です。AngularJS上で動作させるために、(1)と(3)のような定義が必要になっていますが、ここは深く理解する必要は現時点ではありません。これだけのコードだけで、JavaScript側でのフレームワークについての理解もあまり必要なく、サーバサイドで行っているようなデータの差し込みができます。
ここまでで非常に大切なことは、サーバ側にてHTMLの要素については一切、何も処理を行っていないことです。
これによって、サーバサイドのHTMLテンプレートで運用していたときに生じていた以下の問題が解決しやすくなったはずです。
- HTMLコード部分の変更作業においてサーバサイド側に与える影響への懸念
- サーバサイド側で作成する独自のHTMLシンタックスを覚える必要性と、採用するサーバサイドテクノロジー毎に異なるHTMLテンプレートへの対応
AngularJSでのHTMLテンプレートは、サーバサイドでのHTMLテンプレートでのシンタックスに似ているため、これまでの作業と似ていてわかりやすいというメリットもありますが、問題もあります。というのも、サーバ側でのテンプレートエンジンによっては、シンタックスがかぶってしまい、本来、クライアントで処理すべきコードがサーバ側で処理されてしまう可能性があるということです。
たとえば、PHPで最近よく使われているLaravelフレームワークでのBladeテンプレートでは、同じ記述方法があるためにリストXXのようにエスケープをしなければいけません。
@{{ user.id }}.
これでは先のHTMLコード部分でサーバサイド側が与える影響がなくなったとはいえません。そのようなケースが大きな懸念になる場合には、AngularJSではなく、KnockoutJSというHTMLでのシンタックスを崩さずに使えるフレームワークを採用すれば問題ありません。
KnockoutJSを使った場合の例
実際にKnockoutJSを使った場合のコードの例がリスト4です。多少の違いがありますが、おおよそ同じコードのようになることがわかると思います。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>STEP1 - KnockoutJS</title> <script type='text/javascript' src='https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js'></script> <script type="text/javascript" src="script.js"></script> <script><?php $json = array( // :(省略) ); printf("var json = '%s';", json_encode($json, JSON_UNESCAPED_UNICODE)); ?></script> </head> <body> <table border="1"> <tr> <td>ID</td> <!--(1)ラベルの表示 --> <td><span data-bind="text: user.id"></span></td> </tr> <tr> <td>名前</td> <!--(2) 入力要素での値の指定方法 --> <td><input type="text" data-bind="value: user.name"></td> </tr> </table> </body> </html>
AngularJSでの場合とほぼ同様な考え方で記述できます。ただし、各要素への値の設定する記述方法が異なります((1)と(2))。また、少々違いがわかりにくいですが、data-bind属性で「text:~」として指定するか、「value:~」として指定しているかが違います。また、AngualrJSの時にあった特別な記述もなくなっています。HTMLテンプレート内で差し込み用の変数として使えるとようにするJavaScriptコードが以下のリスト5です。
document.addEventListener("DOMContentLoaded", function(event) { var data = JSON.parse(json); ko.applyBindings(data); // data変数をHTMLテンプレートで使えるように設定 });
こちらは、AngualrJSよりも多少シンプルになりましたが、ほとんど違いがないことがわかります。
KnockoutJSは文字を挿入するにもspanタグを記述しなければいけないなど少々面倒ではありますが、AngularJSのようにHTMLタグの属性として記述する形式のためにサーバサイドのテンプレートエンジンとシンタックスが被ることはほぼありません。また、AngularJSを含めほかのフレームワークに比べると、HTMLテンプレートに特化したライブラリという位置づけでもあります。
AngualrJSもKnockoutJSもほとんど同じような仕組みで実現していますが、HTMLのシンタックスを拡張して実現しているか、または、HTMLのシンタックス内で実現しているかで大きな違いがあります。
現在、主流となっているクライアント側のHTMLテンプレートはHTMLのシンタックスを拡張する方向が多いため、HTMLのシンタックスを拡張したものも選択できたほうが有利です。HTMLシンタックスの問題がどうしても回避できないようなケースでは、KnockoutJSを選択すればよいと思います。
サンプルコードの注意点
ただし、これらのコードは説明上わかりやすくしているために、少々問題があります。それは、変数に改行コードやシングルクォートなどの値が含まれていると正しく処理ができないという点です。実際には、base64などで一手間加えることをおすすめします。そのようにすることで、ブラウザ上からHTMLコードを見ても人の目ではわかりにくくする効果もあります。