Do You PHP はてブロ

Do You PHPはてなからはてブロに移動しました

JsRenderチュートリアル - ループ内のタグブロック内でインデックスが取得できない

JavaScript製でjQuery非依存なテンプレートエンジンであるJsRender/JsViewsを使ったチュートリアルを書いてみようと思います。
以前に書いたエントリJsRender入門 - Do You PHP はてなも参照してください。

今回やること

テンプレート内でループ内のタグブロック内でインデックスが取得できない場合の対処

コード

<html>
<body>

<!-- レンダリング結果を表示するスペース -->
<div id="result"></div>

<script src="https://code.jquery.com/jquery-1.11.1.min.js" type="text/javascript"></script>
<script src="http://www.jsviews.com/download/jsviews.min.js" type="text/javascript"></script>
<script type="text/javascript">
// ループ - forタグ
$(function() {
    // 表示するデータ
    var fruits = {
        items: [
            { name: "りんご", num: 3 },
            { name: "いちご", num: 5 },
            { name: "バナナ", num: 2 }
        ]
    };

    // テンプレートに名前(ここでは"linkTemplate")をつけて登録する
    $.templates({
        linkTemplate: "#template"
    });

    // テンプレート"linkTemplate"に変数fruitsを渡し、
    // レンダリング結果を表示
    $('#result').html($.render.linkTemplate(fruits));
});
</script>

<!-- テンプレート -->
<script id="template" type="text/x-jsrender">
{{for items}}
    <p>インデックス={{>#index}}</p>
    {{if #index > 0}}
        <p>インデックス"{{>#index}}"{{>name}}{{>num}}個あります</p>
    {{/if}}

    {{if #index > 0}}
        <p>インデックス"{{>#getIndex()}}"{{>name}}{{>num}}個あります</p>
    {{/if}}

    {{if #index > 0 ~idx=#index}}
        <p>インデックス"{{>~idx}}"{{>name}}{{>num}}個あります</p>
    {{/if}}
{{/for}}
</script>
</body>
</html>

説明

forタグなどのループ内でも当然ifタグ等が使えますが、このタグブロック内でループの#indexを使用すると

Unavailable (nested view): use #getIndex()

というエラーになります。

JsRenderでは、記述されたテンプレートは実際にはViewと呼ばれるオブジェクトに変換され、タグごとにネストした構造になっています。ループのインデックスを表す#indexは、このViewオブジェクトが配列もしくはオブジェクトを表す場合にのみ数値がセットされる仕組みになっていて、ifタグを表すViewオブジェクトなど、それ以外の場合はエラーメッセージがセットされます。

解決方法は少なくとも2つあります。
1つ目は、メッセージに表示される"#getIndex()"を#indexの代わりに使用することです。これは、インデックスを再評価して返すものです。

    <p>インデックス"{{>#getIndex()}}"{{>name}}{{>num}}個あります</p>

もう一つは、JsRenderチュートリアル - 他のテンプレートを読み込む際に変数を渡す - Do You PHP はてなにあるように、変数をネストした子Viewオブジェクトに渡すというものです。JsRenderチュートリアル - 他のテンプレートを読み込む際に変数を渡す - Do You PHP はてなでは子テンプレートに渡しましたが、ifタグなど他のタグでも使用できます。上のコード例では、#indexを~idxとして子Viewオブジェクトに渡しています。

    {{if #index > 0 ~idx=#index}}
        <p>インデックス"{{>~idx}}"{{>name}}{{>num}}個あります</p>
    {{/if}}