Backbone.jsで外部ファイルをテンプレートに指定する方法

このエントリはBackbone.js Advent Calendar11日目です。

皆様素晴らしいエントリを書かれている中、何か初歩的なエントリでスミマセン。

Backbone.jsで作られたサイトを見ると、よく_.templateを使ってHTMLを呼び出す記述を見かけますね。

よく知られているように、_.templateには文字列を渡す方法とtext/templateセクションをセレクタで渡す方法があります。

文字列で渡す
_.template('<div><%= name %></div>',name)

テンプレートセクションをセレクタで指定する
_.template($('#item-template').html(),name)

item-templateのテンプレートセクション
<script type="text/template" id="item-template">
    <div class="view">
<input class="edit" type="text" value="<%= name %>" />
    </div>
</script>

テンプレートセクションの記述はunderscore.jsの呼び出し元HTMLである必要があります。

ところでサーバサイドのMVCフレームワークに慣れ親しんだ人はこれがちょっと違和感で、できればテンプレートを分離して別ファイルにしてviewsフォルダとかにまとめたい欲望に駆れたことはないでしょうか。
しかしながら御存知の通りクライアントサイドJavascriptは表示しているHTMLのDOMに対する操作を主に行うためのもので、別ディレクトリの要素については全く関与しません(当たり前)。

でもデザイナーと分業したり、テンプレートセクションが増えすぎてソースが汚くなるのは嫌だから分離させたら何かと便利じゃない?、という人のために、viewsフォルダを分けて管理する方法について、敢えて考えてみたいと思います。

まずテンプレートを格納するためのフォルダを作成します。フォルダはAJAXでJSからアクセスできる必要があり、今回は/viewsとします。

index.htmlはbackbone.js, underscore.jsの呼び出し元
index.html
views/ここにテンプレートファイルを入れる

次に指定されたファイル名でviewsフォルダからHTMLを取得する関数を作成します。名前は何でもいいので今回は_renderという名前にしました。
2度めからのアクセスはキャッシュから参照させるため、tmpl_cacheというキャッシュ格納用変数も用意します。

var tmpl_cache;
function _render(tmpl_name) {
    if ( !tmpl_cache ) {
        tmpl_cache = {};
    }
    if ( ! tmpl_cache[tmpl_name] ) {
        var tmpl_url = 'views/' + tmpl_name + '.html';
        var tmpl_string;
        $.ajax({
            url: tmpl_url,
            method: 'GET',
            async: false,
            dataType: "html",
            success: function(data) {
                tmpl_string = data;
            }
        });
        tmpl_cache[tmpl_name] = tmpl_string;
    }
    return tmpl_cache[tmpl_name];
}

_renderは拡張子を省略したファイル名を渡すと、viewsディレクトリからHTMLを取得して文字列を返す関数です。
一度呼び出したテンプレートはキャッシュ変数に保持して二回目のアクセスはAJAX通信が発生しないようにします。またacyncをfalseにして強制的に同期処理させます。

View側からは_.templateの第一引数に_renderを指定します。

_.template(_render("item-template"),name)

viewsにitem-template.htmlを作成します。

views/item-template.html
<div class="view">
    <input class="edit" type="text" value="<%= name %>" />
</div>

_renderがAJAX通信して取得した結果を、HTMLとして_.templateに渡す感じになります。これで別ディレクトリに作成したテンプレートを_.templateに渡すことができるようになりました。

しかしながらこの方法、テンプレート取得のたびに無駄なAJAX通信が発生するため、WEBサイトでやる意味はほとんどないと思います。
ですのでPhoneGap+Backbone.jsでアプリを作る時などローカル限定での呼び出しに留めておくのが良いと思います。

というわけで、Backbone.jsほとんど関係ないエントリになってしまいましたが、以上よろしくお願い致します。