SNBinderを試してみる

Life is beautiful: JavaScript HTMLテンプレートエンジン SNBinder 公開 で紹介されていたSNBinderが面白そうなので試してみる。

サンプルの内容

ブログっぽい何か。

サーバ側は、手元の環境が整えやすいのでJSPだけど、そこはどうでもいい。

/snbinder/index.html

HTML。左カラムに「ようこそ」メッセージ、右カラムにブログ記事の一覧が表示される。

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title>test</title>
<style type="text/css">
#navi { float: left; width: 20%; padding: 10px; }
#content { margin-left: 20%; }
.entry { border: 1px black solid; padding: 10px; margin: 10px; }
</style>
</head>
<body>
<div id="navi">
<div id="navi-head">Navigation:</div>
<div id="greeting"></div>
</div>
<div id="content">
<div id="content-head">Recent blog entries:</div>
<div id="entries"></div>
</div>
</body>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
<script type="text/javascript" src="/snbinder/assets/snbinder-0.5.3-modified.js"></script>
<script type="text/javascript" src="/snbinder/assets/index.js"></script>
</html>

snbinder-0.5.3-modified になっている点については後述。

/snbinder/user/info.jsp

ユーザ情報のJSONデータを返すだけ。

<%@ page language="java" contentType="application/json; charset=utf-8"
    pageEncoding="utf-8"%>
{"name": "Leonardo da Vinci"}
/snbinder/blog/entries.jsp

ブログエントリ一覧のJSONデータを返す。

<%@ page language="java" contentType="application/json; charset=utf-8"
    pageEncoding="utf-8"%>
[
  {
    "id": "2",
    "content": "I know that many will call this useless work.",
    "posted_at": "2011/01/23 23:59:59"
  },
  {
    "id": "1",
    "content": "Hello, world!",
    "posted_at": "2011/01/22 00:00:00"
  }
]
/snbinder/assets/template.htm

テンプレート。

{%}greeting{%}
<p>Hello, <b>$(.name)!</b></p>

{%}blog_entry{%}
<div class="entry">
<div class="index">entry #<b>$(.id)</b></div>
<div class="text">
<b>$(.content)</b>
</div>
<div class="status">
posted at <b>$(.posted_at)</b>
</div>
</div>
  • $(.name) のような箇所が、JSONデータが埋め込まれる箇所。先頭のドットは、エスケープすることを示す。ドットではなくアンダーラインを使うと、エスケープされない。
  • テンプレートは上記のように「{%}セクション名{%}本体」のようにセクションを分けて1ファイルに複数入れられるらしい。上の例はちょっとアレだけど、リクエスト回数削減のほかにも、「ヘッダ」「コンテンツ」「フッタ」のようにセクションを分けてコンテンツ部分のみ後述の bind_rowset で複数回テンプレートを適用する、といった使い方も想定されているっぽい。
/snbinder/assets/index.js

実際にテンプレートエンジンでビューを構築する部分。

var debug = { delay: 0 };

$(function(){
  //SNBinder.init({});

  SNBinder.get_named_sections("/snbinder/assets/template.htm", null, function(templates) {
    SNBinder.get("/snbinder/user/info.jsp", null, true, function(json) {
      // bind() は、テンプレートの "greeting" セクションに
      // info.jsp が返したJSONデータを埋め込んで返す
      $("#greeting").html(SNBinder.bind(templates["greeting"], json));
    });

    SNBinder.get("/snbinder/blog/entries.jsp", null, true, function(json) {
      // bind_rowset() は、entries.jsp が返したJSONデータ内の各要素を使って、
      // テンプレートの "blog_entry" セクションに対して繰り返し埋め込みを行い、
      // 全ての結果を結合して返す。
      $("#entries").html(SNBinder.bind_rowset(templates["blog_entry"], json))
      
      // 以下の省略形と考えればOK
      // var template_func = SNBinder.compile(templates["blog_entry"]);
      // var html = "";
      // for (var key in json) {
      //   html += template_func(json[key], key);
      // }
      // $("#entries").html(html);
    });
  });
})
  • debugという名前の、上のようなグローバルオブジェクトがないとエラーになる模様。最新バージョンではすでに修正されています。
  • SNBinder.init({}); は、READMEには必要とあるけど、エラーハンドリングやログイン処理のカスタマイズをしないなら、必要ない模様。
  • SNBinder.get_named_sections は、上述の {%} つきテンプレートを読んで、「セクション名」がキー、「本体」が値になったオブジェクトを第三引数のコールバック関数に渡してくれる。
  • 実際の関数名がいくつかREADMEと異なっているので、ソースを読もう。最新バージョンではすでに修正されています。
/snbinder/assets/snbinder-0.5.3-modified.js

注:最新バージョンではすでに修正されています(コメント参照)。

オリジナルから以下を修正した。

@@ -57,7 +57,7 @@ var SNBinder = (function() {
         get: function(url, params, isJson, callback, _options) {
             var options  = {
                 bypass_cache: false,
-                cache_result: true,
+                cache_result: true
             };
             $.extend(options, _options);

@@ -167,6 +167,9 @@ var SNBinder = (function() {
             })();
         }, // end of post
         evaluate: function(json) {
+            if (typeof(json) == 'object') {
+              return json;
+            }
             try {
                 var obj;
                 eval("obj=" + json);
  • 最初のは、IEで文法エラーになるため。
  • 2番目のは、きちんとMIME typeを指定してJSONデータを返した場合、jQueryは自動でパースしてobjectに直すので、こうしないとエラーになってしまうため。

動作結果

以下のようにJSONデータがテンプレートに埋め込まれて表示された(太字の箇所が「動的」な部分)。デザイン能力がまったくないのでとんでもなくしょぼいけど…

IE8とChromeで試したけど、どちらも普通にうまくいった。


どうせ今時のWebアプリケーションならJavaScriptを書かないということはありえないので、いっそのことMVCのVは完全にHTML+JavaScriptのみに集約してしまう、というのはすごく良さげな気がする。HTMLを操作するならJavaScript(というかjQuery)を使うのが一番楽だし、JSTLやらStrutsやらの汚らしいタグを必死こいて使ってHTMLをこねくり回すのに比べたら遥かにシンプルで自然だ。