このブログの更新は Twitterアカウント @m_hiyama で通知されます。
Follow @m_hiyama

メールでのご連絡は hiyama{at}chimaira{dot}org まで。

はじめてのメールはスパムと判定されることがあります。最初は、信頼されているドメインから差し障りのない文面を送っていただけると、スパムと判定されにくいと思います。

参照用 記事

TrimPath JavaScript Templatesの紹介

ひっさびさにJavaScriptの話題。これを読めば JavaScript Templatesが使える(と思う、だいたいは)。

JACAL(檜山/なんば組によるスロー・プロジェクト)との関係で、TrimPath JavaScript Templates(以下JSTと略記)を調べてみました。JSTは、JACALの文脈を離れても面白いと思うので紹介しておきます。

JSTとは、JavaScriptで実装されたテンプレート処理系です。構文や構造は、PHP Smartyと似ています。JSTは、テンプレートとコンテキスト(と呼ばれるデータ)から処理結果として新しいデータを生成します。テンプレートとコンテキストはHTMLページに最初から埋め込んでおいてもいいし、XMLHttpリクエストで動的に取り寄せてもいいですね(いわゆるひとつのAjaxですか)。

内容:

  1. まずは実例
  2. 処理の概要
  3. テンプレート
  4. テンプレートオブジェクトとコンテキスト
  5. JSTの関数/オブジェクト/メソッド

●まずは実例

以下のサンプルは、TrimPath JSTのサイトにある例とほとんど同じものです。まー、とりあえず試してみてください(Process! というボタンを押してみる):

実際の動きとソースを見比べてもらえれば、だいたいの見当は付くでしょう。

テキストエリアの中身のテキストがテンプレートです。このテンプレートに含まれるプレイスホルダー(置換指定)や制御構造が処理されて、その結果がボタンのすぐ下に挿入されます。置換処理のときに使われるコンテキストと呼ばれるデータは次のとおりです(data1とdata2がコンテキストとして使われる)。


// コンテキスト その1
var data1 = {
products : [ { name: "mac", desc: "コンピュータ",
price: 1000, quantity: 100, alert:null },
{ name: "ipod", desc: "音楽プレイヤー",
price: 200, quantity: 200, alert:"販売中!" },
{ name: "cinema display", desc: "スクリーン",
price: 800, quantity: 300, alert:"お勧め!" } ],
customer : { first: "太郎", last: "皆川", level: "gold" }
};
// コンテキスト その2
var data2 = {
products : [],
customer : { first: "花子", last: "大山", level: null }
};

2つのボタンから呼ばれている関数は、process(date1)とprocess(data2)というものです。念のため、processの定義コードを引用しておけば:


function process(data) {
var result = TrimPath.processDOMTemplate("input-template", data);
var outputHTML = document.getElementById("output-html");
outputHTML.innerHTML = result;
}

process内部でTrimPath.processDOMTemplate関数(これは後で説明する)を呼び出しています。そして、このprocess関数にコンテキスト(例ではdata1とdata2)が渡されるのです。

●処理の概要

JST処理系は、一種のコンパイラになっています。その処理手順は:

  1. parse関数によって、テンプレートをコンパイルしたテンプレートオブジェクトを生成する。
  2. コンテキストを引数として、テンプレートオブジェクトのprocessメソッドを呼ぶことにより処理結果を得る。

次のような感じですね。


var templateString = "${greeting}, ${friend}.";
var context = {greeting:"Hello", friend:"Taro"};

var templateObject = TrimPath.parseTemplate(templateString);
var resultString = templateObject.process(context);

この例では、resultStringが "Hello, Taro." となります。JST関数/メソッドの使い方は、後でもう少し詳しく述べます。

●テンプレート

テンプレートは、JSTを含むテキストです。

文は「{」と「}」で囲んで記述します。if文for文があり、これで分岐制御と繰り返しが書けます。先の実例から抜き出すと:


{for p in products}
...(pに関して繰り返す部分)
{forelse}
...(productsがnull/空のとき使う部分)
{/for}

{if customer.level == "gold"}
...(customer.level == "gold" のとき使う部分)
{else}
...(それ以外のとき使う部分)
{/if}

forelseは見慣れない構文ですが、ループが回らないときの処理を(if文を併用せずに)for文だけで書けるので便利です。

式は「${」と「}」で囲んで記述します。式の構文はJavaScripの式をそのまま使います。例:${customer.last}${customer.first}さん、ようこそ。 JavaScriptの式は、コンテキスト・オブジェクトをスコープとして評価されます。例えば、data1がコンテキストのとき、${customer.last}は、data1.customeer.lastの値で置換されるのです。

式のなかに'}'が含まれるとそこで式が打ち切られてしまうので、そのときは「${%」と「%}」を使いましょう。例えば、${% productName(code, {lang:'ja', detail:true }) %}のように。

特殊記号「{」「}」「$」をエスケープする方法は特に用意されてないようですが、'{'が出現しても直後に'if'などのキーワードが続かない場合(JST文として解釈できない場合)は、そのまま出力されます。'$'の直後に'{'が来ないときも単なるドル記号と解釈、$${p.price}がその例。どうしても問題が起きてしまうときは、コンテキストを{lbrace:'{', dollar:'$' /* その他の定義 */}と定義して、${lbrace}${dollar}などを使えばいいでしょう。

その他に、var文、macro文、cdata文、eval文、minify文があります。また、式には修飾子(modifier)を付けられます(${p.name|capitalize}のcapitalizeは修飾子です)。これらについては次を参照してください。

●テンプレートオブジェクトとコンテキスト

テンプレートをparse(ある種のコンパイル)するとテンプレートオブジェクトが得られます。テンプレートオブジェクトが持つ唯一のメソッドがprocessで、processの引数として渡すオブジェクトがコンテキストです。

processは、コンテキストを使って式の評価をして、分岐や繰り返しをしながら置換処理を実行します。処理結果はテキスト(文字列)です。いったんテンプレートオブジェクトを作ってしまえば、同じテンプレートに複数のコンテキストを適用する処理を効率的に行うことができます。


var templateString = "${greeting}, ${friend}.";
var contexts = [
{greeting:"Hello", friend:"Taro"},
{greeting:"こんにちは", friend:"花子"},
{greeting:"げんきー", friend:"ミカリン"}];

var templateObject = TrimPath.parseTemplate(templateString);
var result;
for (var i = 0; i < contexts.length; i++) {
result = templateObject.process(contexts[i]);
alert(result);
}

コンテキストには任意のJavaScriptオブジェクトが使え、コンテキストがテンプレート内の式のスコープとなります。式から参照されているデータがコンテキストに存在しないときは評価エラーです。

JSTの関数/オブジェクト/メソッド

JSTが提供する関数/オブジェクト/メソッドをJava風の記法で列挙しておきます。ブラケットで囲まれた引数は省略可能です。



まず、テンプレートをparse(コンパイル)する関数:

  • TemplateObject TrimPath.parseTemplate(String template, [String name]);

引数nameはデバッグ時の参照用です。



HTMLページ内で使うときには、HTML要素の内容をテンプレートとみなすのが便利なので、要素IDを引数にするparse関数があります。テンプレートを収める要素には、スタイルが display:none に指定されたtextareaを使うことが多いようです。

  • TemplateObject TrimPath.parseDOMTemplate(String elementId, [Document document]);

引数documentは、目的の要素が含まれる文書です。



TemplateObjectのメソッドは:

  • String process(Object context, [Object flags]);

引数flagsについては、JSTサイトの文書を見てください。



次の関数は、parseとprocessを一度にやってくれる便利関数です。

  • String TrimPath.processDOMTemplate(String elementId, Object context, [Object flags], [Document document]);



これ以上詳しい情報は、http://trimpath.com/project/wiki/JavaScriptTemplateAPI へ。

※ 補足が http://d.hatena.ne.jp/m-hiyama/20051202/1133487974 にあります。