何度やっても同じ

ただの日記

JavaScript(jQueryプラグイン)のテンプレートエンジンjTemplatesを使う

サーバは基本的にJSONで応答を返してくるとして、それを元にブラウザ側でDOMを構築する作業をどうするかということで。jTemplateをつかってみた。

基本

適当に検索してサンプルコードをさがすと、次のような使い方が多くヒットする。$("#result") に、テンプレートとデータのマージ結果が出力される。

var data = { name: "jTemplates" };
$("#result").setTemplate("<p>Hello, {$T.name}</p>");  
$("#result").processTemplate(data);

Templateオブジェクトをあらかじめ作成して使いまわし

でもこれだと、$('#result') 以外の要素にもテンプレートを使いまわしたいときに、テンプレートの解析処理が何度も発生するので、そういう場合は、$.createTemplate() 関数をつかって、あらかじめTemplateオブジェクトを作っておくのがよさそう。

var data = { name: 'jTemplate' };
var tpl = $.createTemplate("<p>Hello, {$T.name}</p>");
$("#result").setTemplate(tpl);
$("#result").processTemplate(data);

ちなみに、最初に紹介したやりかたで要素に直接テンプレート文字列を setTemplate した場合に、その要素からTemplateオブジェクトを取り出すこともできる(以下)が、あらかじめ $.createTemplate しておくほうがスマートでしょう。

var data = { name: "jTemplate" };
$("#result").setTemplate("<p>Hello, {$T.name}</p>");  
$("#result").processTemplate(data);
var tpl = $.getTemplate($("#result"));
// tpl を使いまわし
$("#result2").setTemplate(tpl);  
$("#result2").processTemplate(data);

ネットワークからテンプレートを取得する

ところで、利用するテンプレートエンジンを選定する際に興味を引いたもののひとつが、テンプレートをネットワーク上から取得する機能。ネットワークからロードしたテンプレートをつかってTemplateオブジェクトを生成するには以下のように書く。他に、$("#result").setTemplateURL("/hello.tpl") という関数もある。

var data = { name: "jTemplate" };
var tpl = $.createTemplateURL("/hello.tpl");
$("#result").setTemplate(tpl);
$("#result").processTemplate(data);

が、戻り値としてTemplateオブジェクトが返ってくるというAPIから想像できるとおり、このテンプレート取得処理は、同期通信(async: false)で実行される。ちょっと嫌だな。というわけで、createTemplateURL の利用はやめた。では代わりにどうしたかというと、最初に読み込むHTMLページにテンプレートをインラインで埋め込んでおくことにした。

テンプレートをインラインでHTMLに埋め込む

テンプレートの埋め込みにはtextareaを利用する。

<body>

  <div id="result"></div>

  <textarea id="tpl" style="display:none">
<p>Hello, {$T.name}</p>
  </textarea>

</body>
var data = { name: "jTemplate" };
var tpl = $.createTemplate($("#tpl").val());
$("#result").setTemplate(tpl);
$("#result").processTemplate(data);

ところで、このHTMLの書き方はXHTML的にはValidでないようなので、XHTML1.1 Validな書き方をしたい場合は、次のように書く(jTemplateサイトより)。

1) Simple:
<textarea id="name" style="display:none">
        ... templates ...
</textarea>


2) Valid XHTML 1.1 (using CDATA):
<p style="display:none"><textarea id="name" rows="0" cols="0"><![CDATA[
        ... templates ...
]]></textarea></p>


3) Valid XHTML 1.1 (using comments; suggested):
<p style="display:none"><textarea id="name" rows="0" cols="0"><!--
        ... templates ...
--></textarea></p>

ちなみに、

var tpl = $.createTemplateElement("tpl");

のような使い方を当初は期待したけれども、createTemplateElement などという関数はなぜか存在しない。$.getTemplate(Element) 関数がそれに相当するのかと最初は思ったけれど、この関数については前述したとおりで、使い方が全然ちがう。$.fn.setTemplateElement(elementId) という関数はあるんだけどな。

テンプレートのインクルード

テンプレートエンジン選定時に注目した機能のもうひとつがこれ。

<body>

  <div id="result"></div>

  <textarea id="tpl" style="display:none">
<ul>
{#foreach $T as person}{#include t1 root=$T.person}{#/for}
</ul>
  </textarea>

  <textarea id="greetingTpl" style="display:none">
<li>Hello, {$T.name}</li>
  </textarea>

</body>
var data = [
  { name: "a" },
  { name: "b" },
  { name: "c" }
];
var greetingTpl = $.createTemplate($("#greetingTpl").val());
var tpl = $.createTemplate($("#tpl").val(), { t1: greetingTpl });
$("#result").setTemplate(tpl).processTemplate(data);

#include の root には、インクルードするテンプレート内で $T となる値を指定する。"t1" としているところは自由に名前をつけてよい。$.createTemplate の第2引数をここでつけた名前に合わせさえすればOK。

こうしておけば、greetingTpl だけを単独で使うこともできる。これは便利。

マルチテンプレート機能

jTemplateサイトのサンプルを引用すると、次のとおり。

*** main template *** (all part outside templates are invisible}
{#template MAIN}
 <div>
  <div>{$T.name.bold()}</div>
  {#include table root=$T.table}
 </div>
{#/template MAIN}

-----------------------------------------

*** main table ***
{#template table}
 <table>
  {#foreach $T as r}
  {#include row root=$T.r}
  {#/for}
 </table>
{#/template table}

-----------------------------------------

*** for each row ***
{#template row}
 <tr bgcolor="{#cycle values=['#AAAAEE','#CCCCFF']}">
  <td>{$T.name.bold()}</td>
  <td>{$T.age}</td>
  <td>{$T.mail.link('mailto:'+$T.mail)}</td>
 </tr>
{#/template row}

そしてこのテンプレートファイルをネットワークから取得して使う。

$("#result").setTemplateURL("example_multitemplate1.tpl");
$("#result").processTemplate(data);

しかしね、{#template table} や {#template row} で名前をつけたテンプレートを単独で取り出す方法ってないのかな。それがなければ、この機能は、コードを分割して多少読みやすくすること以外のメリットがないよね。マルチテンプレートとネットワークからのテンプレ取得を組み合わせて利用することを当初ちょっと考えたのだけど、この両機能はどうも使いものにならない。

もしこれが使いたいなら、ネットワークから取得するテンプレートファイル内にtextareaを複数定義することでマルチテンプレート的な機能は実現できるし、そっちのほうが柔軟性があってよいとおもう。