JavaScriptの自作テンプレートシステム

JavaScriptのテンプレートシステム...としては有名どころに AjaxPages というのがある.読み込みがすこし遅れるとうまくいかなかったりする(特にIE系).

同じようなものにEJSというのもある.

こちらは今現在(2009/9/27)Ver0.9alpha だが、JavaScriptMVC の一部に統合され、コードがかな〜り複雑になってきており、とても気楽にいじれるものではなくなりつつある。

AjaxPages のほうはまだ簡単なコードなのでAjax通信部分をjQuery化するなども簡単.

ただ、それではあまり面白みがない.そこで、複雑化する前のEJSの初期コードが手もとに残っていたので、それを利用して JavaScriptのテンプレートシステムを自作してみた.自作といっても、もとのejs関数の部分は埋め込み記号をRubyのERBと同じ"<%...%>","<%=...%>"が使えるように変更した以外、たいした改造はない.

もともとEJSの初期コードは60数行の実にシンプルなものだったのである.シンプルさでは AjaxPages をもしのいでいた.これをいじらない手はないと考えたわけ.

コードもOOP化して、UIを使いやすいものにした.ついでにもとのEJSには無かった、テンプレートに内部テンプレートを埋め込む関数(AjaxPages での @include のようなもの)も追加した.

 サンプル1

次のサンプルを見て下さい.

<html>
<head>
<script type="text/javascript" src="jquery-1.3.2.min.js"></script>
<script type="text/javascript" src="viewTemplate.js"></script>
</head>

<body>
<b>Output:</b><br/>
<div id="output" ></div>
<body>
</html>
<script type="text/javascript">
var view1 = ViewTemplate();
view1.loadProcessor({ url: "hello01.tmpl" });
// alert(view1.getProcessor());
view1.show({
  place: $('#output'),
  context:  { name: 'JavaScript Template' }
});
</script>
var view = ViewTemplate();
view.loadProcessor({ url:"テンプレートファイルのURL" });

でテンプレート内に埋め込まれたJavaScriptコードを実行しながら、テンプレートをレンダリングする関数が作られる。上記alertのコメントアウトをはずせば、getProcessorメソッド で得られたレンダリング関数のコードを見ることもできる.

ここではjQueryのオブジェクトで、ID='#output' の場所にレンダリング結果を表示させている.

showメソッドの引数で、placeに表示するDOM、contextにJavaScriptオブジェクトを渡せば、テンプレート内で変数名contextでバインドされたデータを参照できる.

以下サンプル1で読み込んだテンプレート hello01.tmpl

[hello01.tmpl]

<% var hello = "Hello World!"; %>
<h2><%= hello %></h2>
<h3>Hello <%= context.name %>!</h3>

 サンプル2

buildメソッドでテンプレートのレンダリング結果を得る方法。これだとレンダリングしたHTMLを直接変数などに格納することもできる.

var view2 = ViewTemplate();
view2.loadProcessor({ url: "hello02.tmpl" });
var data = { name: 'ViewTemplate', name2: 'Inner Template' };
$('#output').html( view2.build({ context: data }));

ここでの hello02.tmpl には内部テンプレート埋め込み関数を利用している.

[hello02.tmpl]

<% var hello = "Hello World!"; %>
<h2><%= hello %></h2>

<h3>Hello <%= context.name %>!</h3>
<%= $include("inner.tmpl", context) %>

$include("内部テンプレート名", データ) でテンプレート内で他のテンプレートのレンダリング結果を埋め込む.

コードを見てもらえばわかるが、$include という関数名は、ViewTemplate().includeTool() の結果返される関数を格納しているだけなので、別に他の名前に変更してもOK.

$include = ViewTemplate().includeTool();

 viewTemplate.js

//////////////////////////////////////////
// ViewTemplate Application
// use jQuery Ajax
//////////////////////////////////////////

var ViewTemplate = function() {
  var self = {};
  var processor = null;
  // ejs cord
  var EJS_SYNTAX = [/<%(?!=)(.+?)%>/, /<%=(.+?)%>/];
  var get_renderer = function( templateStr ) {
    var result = [];
    function output(text) {
        if (text) {
            result.push("output.push('" +
                        text.replace(/\\/g, "\\\\").replace(/'/g, "\\'") +
                        "');");
        }
    }
    function search(str, re) {
        var nearest = str.length;
        for (var i = 0; i < re.length; i++) {
            var pos = str.search(re[i]);
            if (pos >= 0 && pos < nearest) {
                nearest = pos;
                nearestRe = re[i];
            }
        }
        if (nearest >= 0 && nearest < str.length ) {
            var prefix = str.substring(0, nearest);
            str = str.substring(nearest);
            var r = nearestRe.exec(str);
            str = str.substring(r[0].length);
            return [prefix, r, str];
        }
        return null;
    }
    result.push("var output = [];");
    var lines = templateStr.split('\n');
    for (var i = 0; i < lines.length; i++) {
        var line = lines[i];
       
        while (true) {
            var test = search(line, EJS_SYNTAX);
            if (test) {
                output(test[0]);
                var codeType;
                if (/<%=/.test(test[1][0].substring(0, 3))) {
                  codeType = test[1][0].substring(0, 3);
                } else {
                  codeType = test[1][0].substring(0, 2);
                }
                if (codeType == '<%') {
                    result.push(test[1][1]);
                }
                else if (codeType == '<%=') {
                    result.push('output.push(' + test[1][1] + ')');
                }
                line = test[2];
            }
            else {
                output(line);
                break;
            }
        };
        result.push('output.push("\\n");');
    }
    result.push("return output.join('');");
    var renderer = new Function('context', result.join('\n'));
    return renderer;
  }

  var load_template = function( url ) {
    var req, templstr;
    req = $.ajax({ url: url, async: false })
    templstr = req.responseText;
    return templstr;
  };
  self.getProcessor = function() {
    return processor;
  };
  self.loadProcessor = function( opt ) {
    var templ = load_template( opt.url );
    processor = get_renderer( templ );
    return self;
  };
  self.show = function( opt ) {
    opt.place.html( processor( opt.context ) );
  };
  self.build = function( opt ) {
    return processor( opt.context );
  };
  self.includeTool = function() {
    return function( url, context ) {
      var a_view = new ViewTemplate;
      a_view.loadProcessor({ "url": url });
      return a_view.build({ "context": context });
    };
  };

  return self;
};

$include = ViewTemplate().includeTool();

aBowman

別荘はこちら

  • Marbles2
    音楽、美術、映画、本など趣味的なページはここに移転しました。考えるのが面倒だったので、タイトルは単に2です。
2017年6月
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30  

mail

  • 82pkdick@gmail.com

最近のトラックバック

無料ブログはココログ