Javascriptのテンプレートエンジンっぽいものはいくつかあるみたいですが、今回はjQueryプラグインのjTemplatesを使ってみてちょっとはまったところをメモしておきます。
Javascriptのテンプレートエンジンについてはこちらでまとめられています。
http://tanarky.blogspot.com/2009/11/javascript-template-engines.html
jTemplatesの本家ページ
http://jtemplates.tpython.com/
試しにYahooのAPIを叩いてみます。
サンプルのソースコードは以下の通り
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript" src="http://jtemplates.tpython.com/jTemplates/jquery-jtemplates.js"></script>
<div id="test_result"></div>
<script type="text/javascript">
jQuery(function(){
var appid =
jQuery.ajax({
url: "http://shopping.yahooapis.jp/ShoppingWebService/V1/json/itemSearch",
data: {
"appid": "cPvQ.eCxg67GFHywsAFH2KW2bVkOlYSz_pBz9.V3sOefgLF09GM8gJw8PJHRqII-",
"query": "amp",
"hits": 5
},
dataType: "jsonp",
cache: false,
error: function(data, status) {
alert(status);
},
success: function(data, status) {
jQuery("#test_result").setTemplate("<table id=\"dataTable\" border=\"1\">" +
"{#foreach $T as item begin=2 count=5}" +
"<tr><td><a href={$T.item.Url}>{$T.item.Name}</a></td>" +
"<td><img src={$T.item.Image.Small} /></td></tr>" +
"{#/for}" +
"</table>");
jQuery("#test_result").processTemplate(data.ResultSet[0].Result);
}
});
});
</script>
これを実際に実行するとこんな感じになります。
でもこれだと&がそのまま残ってしまってしまってます。
おかしいなーと思ってjTemplatesのソースを見てみたら、案の定HTMLエンティティを変換している部分がありました
/**
* Replace chars &, >, <, ", ' with html entities.
* To disable function set settings: filter_data=false, filter_params=false
* @param {string} string
* @return {string}
* @static
* @memberOf TemplateUtils
*/
TemplateUtils.escapeHTML = function(txt) {
return txt.replace(/&/g,'&').replace(/>/g,'>').replace(/</g,'<').replace(/"/g,'"').replace(/'/g,''\
');
ソースをたどっていくと、どうやらsetTemplateするときにオプションで設定できることが分かりました。
/**
* Create new template from string s.
* @name Template
* @class A template or multitemplate.
* @param {string} s A template string (like: "Text: {$T.txt}.").
* @param {array} [includes] Array of included templates.
* @param {object} [settings] Settings.
* @config {boolean} [disallow_functions] Do not allow use function in data (default: true).
* @config {boolean} [filter_data] Enable filter data using escapeHTML (default: true).
* @config {boolean} [filter_params] Enable filter parameters using escapeHTML (default: false).
* @config {boolean} [runnable_functions] Automatically run function (from data) inside {} [default: false].
* @config {boolean} [clone_data] Clone input data [default: true]
* @config {boolean} [clone_params] Clone input parameters [default: true]
* @config {Function} [f_cloneData] Function using to data cloning
* @config {Function} [f_escapeString] Function using to escape strings
* @augments BaseNode
*/
var Template = function(s, includes, settings) {
(※setはTemplateのラッパーメソッド)
というわけで、setTemplateの第二引数にundefined, 第三引数に{filter_data : false}を与えてやるとうまくエンティティが文字として表示されました。
要するに、jTemplateに渡されるデータの中にHTMLエンティティが含まれているとjTemplateが気を利かしてエンコードしてくれるけど、すでにエンコード済みのデータを渡すと二重にエンコードすることになって、結果として&amp;みたいなことになってしまいます。
jTemplatesはエンコードするかどうかのオプションを引数で渡すことができるので、渡すデータによって使い分ける必要がありそうです。