端っこプログラマーの手帳

主にプログラムに関する手記です

JsRender for文内で親要素にアクセス

JsRender にて for文内で親要素へのアクセスが分からず調べてみた。

問題になった状況

ファイル構成

├ jquery-1.9.1.js 
├ jsrender.min.js(JsRender v1.0.0-beta)
└ index.html

index.html のコード

<!DOCTYPE html>
<html>
    <head>
        <title></title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width">
        <script type="text/javascript" src="./jquery-1.9.1.js"></script>
        <script type="text/javascript" src="./jsrender.min.js"></script>
    </head>
    <body>
        <div class="container">
            <div>映画視聴記録</div>
            <script id="template" type="text/x-jsrender">
                {{for movies}}
                <tr>
                   <td>{{>title}}</td>
                   <td>
                       {{for ~statuslist}}
                       <p><input type="radio" name="status{{>no}}" value="{{>status_cd}}">{{>status_name}}</p>
                       {{/for}}
                   </td>
                </tr>
                {{/for}}
            </script>
            <table border='1'>
                <thead>
                <tr>
                <th>作品名</th>
                <th>ステイタス</th>
                </tr>
                </thead>
                <tbody id="movies"></tbody>
            </table>
        </div>
        <script type="text/javascript">
           
            var output = $('#template').render(
               {
                   movies: [
                       {no: 1, title: 'いかレスラー', status_cd:'not'},
                       {no: 2, title: 'コアラ課長',   status_cd:'not'},
                       {no: 3, title: 'かにゴールキーパー', status_cd:'done'},
                   ],
               },
               {
                   statuslist:[
                       {status_cd: 'not', status_name: '未鑑賞'},
                       {status_cd: 'done', status_name: '鑑賞済み'},
                   ]
               }
            );
            $('#movies').append(output);
        </script>
    </body>
</html>

次の2点の理由から、 {{for ~statuslist}}〜{{/for}} の中で、親要素の movies にアクセスする必要があります。*1

・デフォルト値の取得(movies.status_cd)
・一行毎に input の name属性値を変更(movies.no)*2

最終的なイメージはこんな感じ。

f:id:kzhishu:20170603220754p:plain

親要素にアクセスする

ドキュメントに書いてありました。

JsRender/JsViews

「Accessing parent data」とドンピシャの内容です。 「There are several ways to get to parent data:」とあり、方法はいくつかあります。

グローバル領域にセット(Create a contextual parameter)

for文で、~[key] に値をセットすることで、ループ内からアクセスできる。

<script id="template" type="text/x-jsrender">
    {{for movies}}
    <tr>
       <td>{{>title}}</td>
       <td>
           {{for ~statuslist ~no=no ~status_cd=status_cd}}
           <p><input type="radio" name="status{{>~no}}" value="{{>status_cd}}"{{if ~status_cd == status_cd}} checked{{/if}}>{{>status_name}}</p>
           {{/for}}
       </td>
    </tr>
    {{/for}}
</script>

親要素を1つまるごとセット(itemVar)

親要素の for文で、itemVar に ~[key] をセットすることで、配下の要素でアクセスできるようになる。まるごと指定できるため、参照する値が多い時は便利。

<script id="template" type="text/x-jsrender">
    {{for movies itemVar="~movie"}}
    <tr>
       <td>{{>title}}</td>
       <td>
           {{for ~statuslist}}
           <p><input type="radio" name="status{{>~movie.no}}" value="{{>status_cd}}"{{if ~movie.status_cd == status_cd}} checked{{/if}}>{{>status_name}}</p>
           {{/for}}
       </td>
    </tr>
    {{/for}}
</script>

親要素のプロパティにアクセス(view.parent property)

for文でのセットは不要で、直接プロパティにアクセス。書き方は冗長だが一番シンプル。*3

<script id="template" type="text/x-jsrender">
    {{for movies}}
    <tr>
       <td>{{>title}}</td>
       <td>
           {{for ~statuslist}}
           <p><input type="radio" name="status{{>#parent.parent.data.no}}" value="{{>status_cd}}"{{if #parent.parent.data.status_cd == status_cd}} checked{{/if}}>{{>status_name}}</p>
           {{/for}}
       </td>
    </tr>
    {{/for}}
</script>

*1:正確には、movies のループ中の1要素です。

*2:すべて同じラジオボタンのグループとして扱われてしまうため。no は書いてあるがアクセスはできていないです。

*3:ちなみに「Use the view.get(type) method」という方法を使うと、#parent.get("item").data でアクセスできる。