要点
- javascript のテンプレートエンジンは一見便利そうだけどいざ運用となるといろいろ面倒くさそう。
- これは javascript に限った話ではない。すでに、kwartz という解決策が開発されている。
- kwartz を拡張して、javascript のテンプレートエンジン jsrender でも使えるようにしてみた。
本文
javascript のテンプレートエンジンが増えてきたみたいなんで色々調べていました。テンプレートエンジンを使えば WebAPI の出力を簡単に、html に反映させることが可能です。
テンプレートエンジンは、DOMを操作することもないので、html と javascript を疎結合にできるため、デザイン変更にも柔軟に対応できるとされています。ただし、テンプレートエンジンを使用するためには、独自の表記方法を覚える必要があります。
例えば、jsrender ではこんな感じの出力を得るために
<ul id="languages">
<li>ruby</li>
<li>perl</li>
<li>python</li>
</ul>
こんなテンプレートを書く必要があります。
<ul id="languages"></ul>
<script id="languagesTmpl" type="text/x-jsrender">
{{for languages}}
<li>{{>lang}}</li>
{{/for}}
</script>
まず html を書いて見栄えを確認し、その後、それをテンプレートに変換するわけですが、こうした作業は煩雑になりがちです。
デザインが変わるたびにテンプレートを弄るのは苦痛ですし、html のデザインが固まるまで、テンプレートの作成作業を始めることができない、というのでは能率が上がりません。分業体制しているときに デザイナーにテンプレートの書き方を覚えてくれ、というのも酷な話です。(デザイナーが組むプログラマによってテンプテートエンジンも変わるでしょうから)
こうした問題は以前から指摘されていて makotokuwata さんが kwartz というテンプレートシステムを開発してこの問題に対処しています。(詳しくは、スライド「 HTMLデザインを崩さないテンプレートエンジンの作り方 」をどうぞ)
kwartz とは そして kwartz-contrib とは
kwartz は、ruby で書かれていますがアプリケーションの実行時にテンプレートを操作するのではなく、html とプレゼンテーションロジックを独立させ、そこからテンプレートを生成するというアプローチを取っています。生成されるテンプレートとして eruby だけでなく、jsp や php も選択することができます。(詳しくは、ユーザーガイド をどうぞ)
この、 kwartz を拡張して、jsrender のテンプレートを生成できるようにしてみました。名付けて kwartz-contrib
使い方
Gemfile を書き、
source "https://rubygems.org"
gem "kwartz-contrib", :git => "https://github.com/tohosaku/kwartz-contrib.git"
そしてインストール
bundle install --path=vendor/bundle
まず、htmlファイル
<html>
<head>
<script src="http://code.jquery.com/jquery.js"></script>
<script src="http://www.jsviews.com/download/jsrender.js"></script>
</head>
<body>
<h1>Languages</h1>
<ul id="languages">
<li id="mark:lang">ruby</li>
<li class="dummy">perl</li>
<li class="dummy">python</li>
</ul>
<script src="js/main.js" type="text/javascript"></script>
</body>
</html>
次にプレゼンテーションロジックファイル これは kwartz の独自形式です。CSSに似ています。
index.plogic
/* 繰り返しのロジック */
# languages {
logic: {
_stag()
_etag()
<script id="languagesTmpl" type="text/x-jsrender">
{{for languages}}
_cont()
{{/for}}
</script>
}
}
/* 繰り返しの中の要素 */
# lang {
value: lang;
}
.dummy {
logic: {
}
}
kwartz-contrib を実行すると、
bundle exec kwartz-contrib -l jsrender -p index.plogic src/index.html > index.html
このようなテンプレートが生成されます。
<html>
<head>
<script src="http://code.jquery.com/jquery.js"></script>
<script src="http://www.jsviews.com/download/jsrender.js"></script>
</head>
<body>
<h1>Languages</h1>
<ul id="languages">
</ul>
<script id="languagesTmpl" type="text/x-jsrender">
{{for languages}}
<li id="lang">{{>lang}}</li>
{{/for}}
</script>
<script src="js/main.js" type="text/javascript"></script>
</body>
</html>
そして、メインプログラムはこちら。
var langs = {
"languages" : [
{"lang" : "C#"},
{"lang" : "Visual Basic"},
{"lang" : "F#"}
]
};
$(document).ready(function(){
$("#languages").html(
$("#languagesTmpl").render(langs)
);
});
ブラウザの出力を、Firebug かなんかで確認して、以下のようになっていれば成功です。
<html>
<head>
<script src="http://code.jquery.com/jquery.js"></script>
<script src="http://www.jsviews.com/download/jsrender.js"></script>
</head>
<body>
<h1>Languages</h1>
<ul id="languages">
<li>C#</li>
<li>Visual Basic</li>
<li>F#</li>
</ul>
<script src="js/main.js" type="text/javascript"></script>
</body>
</html>
kwartz はアプリケーションを展開する前にテンプレートを生成する仕組みなので実行環境に Ruby が入っている必要はありません。
プレゼンテーションロジックが独自形式というので敷居が高いと感じるかもしれませんが、開発中にデザインの変更があった場合でもデザイン元の html とそれを元にしたテンプレートを見ながらメンテナンスする頻度は激減します。プレゼンテーションロジックファイルでは、一部制限はあるものの クラスセレクタも使えるので、値の反映のような単純な処理はクラスでまとめて定義できます。
ちなみに、本家 kwartz ではプレゼンテーションロジックを html 要素の属性の中に書き込む手法も用意しているのですが、 kwartz-contrib では、そこまでサポートしていません。すいません。中途半端で。