jTemplatesでHTMLを書くときに知っておきたい8つのこと

カテゴリ
ブックマーク数
このエントリーを含むはてなブックマーク はてなブックマーク - jTemplatesでHTMLを書くときに知っておきたい8つのこと
このエントリーをはてなブックマークに追加
こんにちは、(主に)LDサービスのインターフェース部分に携わらせてもらっている油井です。

現在ホットな JavaScript-library の一つとして「jQuery」の名をあげることができますが、このライブラリのプラグインとして動作するjTemplatesというHTMLテンプレートエンジンにも、(個人的にはですが)注目しています。

 http://jtemplates.tpython.com/

そこで、以下、私がjTemplatesを触ることにより知り得た『jTemplatesでHTMLを書くときに知っておきたい8つのこと』について言及していきたいと思います。

※ 今回の記事では「jQuery1.2.1-非圧縮版」そして「jTemplates0.5.0-非圧縮版」を使用することを想定しています。

1, (特殊変数である)$Qの正体

 jTemplatesには$Tと$Pそして$Qという特殊変数が用意されているのですが、オフィシャルページの解説を見るだけでは、$Qの正体がいまいちつかめません。さらに分かりにくくしていることとして、($Qについての)オフィシャルページにある説明には「$Q is XHTML element's attributes」とあるのに、そこで示されているサンプルでは「$Q.version」という使われ方がされていることがあります(この不可解な件についてはソースコードを直接参照すれば分かります)。

$Qの正体はTemplateオブジェクトにあるgetメソッドの第三引数に渡されるHTMLオブジェクト、もしくはjQueryセレクタによりセレクトされたHTMLオブジェクトとなります。

例えば以下のようなHTMLがあったとして
--- 略 ---
<body style="color:red;">
 hoge
</body>
--- 略 ---
その上で以下のようなコードを実行すると
<script type="text/javascript">
 $(function () {
   var jt = $.createTemplate("body {$T.label} is {$Q.style.color}.");
   alert( jt.get({
      label : "color"
   }, [], document.body) );
 });
</script>
以下のような結果が得られます。
body color is red.
※ jQueryセレクタを用いる方法ではセレクタにより選ばれたHTMLオブジェクトが$Qにセットされます。

2, setTemplateURLで取得したテンプレートに含まれる日本語がMac-safariで化ける

setTemplateURLは内部でjQueryコアにある$.ajaxを使っていますが、それは「Mac-Safari-日本語」のことを考慮していません。

対処法:
 http://kawa.at.webry.info/200511/article_9.html

この問題を「jTemplates.js」の側で対処するとした場合の具体的な対処法は、以下のコードをjTemplatesの冒頭あたりに貼付けた上で
String.prototype.safariAjaxPatch = function () {
  var text = String( this ) || "";
  if ( navigator.appVersion.indexOf("KHTML") > -1) {
    var esc = escape( text );
    if ( esc.indexOf("%u") < 0 && esc.indexOf("%") > -1)
      text = decodeURIComponent( esc );
  }
  return text;
}
コード中にある「responseText」を「responseText.safariAjaxPatch()」に書き換えればOKです。

3, URLベースのメソッドで取得したテンプレートの更新内容がIEで反映されない
IEの場合Ajaxを用いた通信を行うと、GETメソッドの場合一度実行されるとデータがキャッシュされて、2回目以降の通信はそのキャッシュされたデータを読みに行くようになってしまいます。
が原因です。対処法はリクエストするURLにユニークな文字列をつけることです。
$.setTemplateURL("/template/jt.tpl?" + Math.random().toString(36).slice(-8))

4, jsオブジェクト(連想配列)の走査はjTemplates標準に用意されている構文だけではできない

perl-TT(Template Toolkit)では、以下のようなコードで連想配列の内容をテーブル形式で出力することができます。
#!/usr/bin/perl
use strict;
use warnings;
use Template;

Template->new->process(\*DATA,{
            hash => {
               n => 1,
               k => 2,
            },
});

__END__
<table>
[% FOREACH item IN hash %]
  <tr>
   <td>[% item.key %]</td>
   <td>[% item.value %]</td>
  </tr>
[% END %]
</table>

jTemplatesにも
{#foreach |VAR| as |NAME|}..{#/for}
という構文がありますが、この構文で走査できるのは配列のみであり、jsオブジェクトは対象外です。それでも同様のことがしたい場合は
function keys (obj) {
  var res = [];
  for ( i in obj ) res.push(i);
  return res;
}
というjsオブジェクトのキー一覧を抜き出す関数を別に定義してからテンプレート側に
<table>
{#foreach keys( $T.hash ) as t}
  <tr>
   <td>{$T.t}</td>
   <td>{$T.hash[ $T.t ]}</td>
  </tr>
{#/for}
</table>
と書けばOKです。

5, jTemplatesでもTTのフィルターのようなものが欲しい

Perl-TTにはフィルターというとても便利な機能があります。それと同様のことをjTemplatesでも実現するには

  1. Stringオブジェクトに拡張メソッドを定義し、それを用いる
  2. (String.prototypeを汚すのが嫌であれば)最も単純な形の関数を定義し、それを用いる

この2つのうちどちらかを選びます。
実例をみせると、例えば何文字になるのか分からないjTemplates変数を10文字に切り詰めたい場合は

 http://jqueryjs.googlecode.com/svn/trunk/plugins/methods/string.js
 ※ Adds trim, camelize, startsWith, endsWith, truncate and stripTags.

をインクルードした上でテンプレートに
 
{$T.name.truncate(10, "...")}

と書けばOKです。

6, テンプレートを一つにまとめた場合、まず最初に処理されるのは「MAIN」と名付けられたテンプレートである

jTemplatesのバージョン0.2から一つのファイルに複数のテンプレートを含ませることができるようになりました。一つ一つのテンプレートは以下の書式に従って書きます。
{#template template-name}
  html-template-body
{#/template template-name}
これが複数ある場合にまず最初に必ずインクルードされるのが「MAIN」と名付けられたものとなります。例えば、
{#template MAIN}
  main template.
{#/template MAIN}

{#template SUB}
  main template.
{#/template SUB}
のようなテンプレートファイルがあった場合に
var jt = $.createTemplateURL("[上記テンプレートファイルまでのパス]");
alert( jt.get({}, [], window) );
を実行すると「main template.」という文字列が得られます。

7, テンプレートを一つにまとめた場合、テンプレート構文の外にあるものは全てコメントとして扱われる

jTemplatesはテンプレートファイルが読み込まれた際に内部で「splitTemplates」というテンプレートの切り分け処理を行うメソッドを呼び出しています。その処理内容を端的に説明するとテンプレートとなる文字列を「/\{#template *(\w*?)\}/g」という正規表現で分割し、分割されたものそれぞれを名前ごとに内部変数へと蓄えるというものです。よって先にあげた正規表現にマッチしないものは全て無視されているので、コメントとして扱われます。
テンプレートを一つにまとめない場合は
{*
 コメント
*}
という書式で書くことによって、コメントとして認識させることができます。

8, 入力フォームに「<」や「>」などを挿入することができない

「<textarea id="txt"></textarea>」で表現されるフォームがあった場合に
$("#txt").setTemplate("<b>hoge</b>");
$("#txt").processTemplate({},[],window);
というコードを実行すれば、(本来ならば)テンプレート処理された後のコードがテキストエリアにフィルインされますが、実際はそうはなりません。
なぜかというと、それはjTemplatesがフィルイン処理を行う場合に、どんなときでもjQueryコアにある「$( xxx ).html」メソッドを用いているからです。
この問題を解決する為には

  1. テンプレート文字列にエスケープ処理を施す(exp: 「<」 => 「&lt;」)
  2. 処理対象となるHTMLオブジェクトが入力フォームの場合であれば「$(xxx).val」メソッドを用いるよう、jTemplatesのコードを書き直す
  3. 作者に報告して直してもらう

この3つのうちどれかを選びます(3番目は冗談のようなものですが、、、)。
2番目の具体的なコードを示すとjTemplates.js(非圧縮版)の711行目にある「jQuery(this).html(t.get(d, param, this));」を
jQuery( this )[ ( jQuery( this ).is("input") || jQuery( this ).is("textarea") )
         ? "val"
         : "html" ](t.get(d, param, this));
に置き換えます。

以上、jTemplatesでHTMLを書く際に知っておいた方がいい8つのことについて言及しました。
今回、記述したのはjTemplates自体の扱い方ではなく、その先にあるHTMLテンプレートについてとなりますが、jTemplates自体の扱い方についても「nowa」にある私の個人ブログで記事としてあげています。

 http://abui.nowa.jp/entry/909b254743

こちらも合わせて参照してもらえば幸いかと思います。
以上です。