2010-01-11

jQueryプラグインjTemplatesのHTMLエンティティ変換処理について

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>


これを実際に実行するとこんな感じになります。





でもこれだと&amp;がそのまま残ってしまってしまってます。
おかしいなーと思って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;amp;みたいなことになってしまいます。
jTemplatesはエンコードするかどうかのオプションを引数で渡すことができるので、渡すデータによって使い分ける必要がありそうです。

1 件のコメント:

ZenBackWidget