JavaScriptのテンプレートJsRenderとJsViewsの紹介

Table of Content

かって、JavaScriptのテンプレートとしてjquery.tmpl.jsが存在した。
jQuery公式サイトにものっていたが、現在はメンテナンスされておらず、公式から削除された。
https://github.com/BorisMoore/jquery-tmpl

しかしながら、同じ作者のJsRender、JsViewsといわれるライブラリが後継として存在している。
このドキュメントでは JsRender, JsViewsについての解説を行う。
http://www.jsviews.com/

JsRender

JsRenderはDOMやjQueryの依存関係なしで、軽量でありながら強力で拡張可能なテンプレートエンジンである。

JsRenderは以下からダウンロードすることが可能だ。
http://www.jsviews.com/#download

実装のサンプルとJsRenderの機能

JsRenderを利用した実装のサンプルを用いてJsRenderの機能を紹介する。

単純なオブジェクトの出力例

ここでは単純なオブジェクトをテンプレートを使用して出力するサンプルをしめす。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"  type="text/javascript"></script>
<script src="./jsrender.min.js" type="text/javascript"></script>

<script id="theTmpl" type="text/x-jsrender">
  <p> Name: {{:name}} </p>
</script>

<script type="text/javascript">
$(function () {
  var data = {
    name: 'Jack'
  };
  var html = $('#theTmpl').render(data);
  $('#result').append(html);
}); 
</script>
</head>
<body>
  <div id="result"></div>
</body>
</html>

出力:

Name: Jack 

まず、text/x-jsrenderというtypeのscript中にテンプレートの記述を行う。

<script id="theTmpl" type="text/x-jsrender">
  <p> Name: {{:name}} </p>
</script>

これはrender functionによりデータとひもづけられてHTMLが作成される。

  var html = $('#theTmpl').render(data);

この時、作成されるHTMLは次のようになる。

  <p> Name: Jack </p>

配列の出力例

先の例ではテンプレートに渡すデータがオブジェクトだった。もし、配列を渡した場合はどうなるであろうか?
先のサンプルを配列に置き換えてみる。

  var data = [
    {name: 'Jack'},
    {name: 'Sara'},
    {name: 'Dick'}
  ];

ここで作成されるHTMLは次のようになる。

  <p> Name: Jack </p>
  <p> Name: Sara </p>
  <p> Name: Dick </p>

データとして配列を渡した場合は、複数のデータが作成されることになる。

もし、渡すデータとしてはオブジェクトであり、そのプロパティが配列だった場合は、テンプレートの中でループをすることで表示することが可能だ。

以下のようなデータがあったとしよう。

  var data = {
    title: 'タイトル',
    names:[
      {name:'Jack'},
      {name:'Sara'},
      {name:'Dick'}
    ]
  };

この時はテンプレートにループを加えるとよい。

<script id="theTmpl" type="text/x-jsrender">
  <h1>Title {{:title}}</h1>
  {{for names}}
    <p>Name: {{:name}}</p>
  {{/for}}
</script>

この時、生成されるHTMLは次の通りになる。

  <h1>Title タイトル</h1>
    <p>Name: Jack</p>
    <p>Name: Sara</p>
    <p>Name: Dick</p>

もし、データの配列中にさらに配列があったらどうであろうか?

  var data = {
    title: 'タイトル',
    names:[
      {name: 'test1', sub:['a','b','c']},
      {name: 'test2', sub:['e','f','g']}
    ]
  };

以下のように、テンプレート中のループの中にさらにループを記述するだけでよい。

<script id="theTmpl" type="text/x-jsrender">
  <h1>Title {{:title}}</h1>
  {{for names}}
    <p>Name: {{:name}}</p>
    {{for sub}}
      <p>subitem: {{:}}</p>
    {{/for}}
  {{/for}}
</script>

この場合の出力結果は以下のようになる。

  <h1>Title タイトル</h1>
    <p>Name: test1</p>
      <p>subitem: a</p>
      <p>subitem: b</p>
      <p>subitem: c</p>
    <p>Name: test2</p>
      <p>subitem: e</p>
      <p>subitem: f</p>
      <p>subitem: g</p>

オブジェクトのプロパティの列挙

オブジェクトのプロパティ名とその値を列挙するサンプルを示す。
以下のようなオブジェクトが存在しており、addressのプロパティ名と値を列挙したいものとする。

  var data = [
    {
      "name": "Pete",
      "address": {
        "street": "12 Pike Place",
        "city": "Seattle",
        "ZIP": "98101"
      }
    },
    {
      "name": "Heidi",
      "address": {
        "street": "5000 Broadway",
        "city": "Sidney",
        "country": "Australia"
      }
    }
  ];

この場合はテンプレートにforではなくpropsを用いる。
すると、>keyでプロパティ名、>propでプロパティの値が取得できる。

<script id="theTmpl" type="text/x-jsrender">
  <h1>Name {{:name}}</h1>
  {{props address}}
    <b>{{>key}}:</b> {{>prop}}<br/>
  {{/props}}
</script>

この時のHTMLの出力は次のようになる。

  <h1>Name Pete</h1>
    <b>street:</b> 12 Pike Place<br/>
    <b>city:</b> Seattle<br/>
    <b>ZIP:</b> 98101<br/>
  <h1>Name Heidi</h1>
    <b>street:</b> 5000 Broadway<br/>
    <b>city:</b> Sidney<br/>
    <b>country:</b> Australia<br/>

文字列をテンプレートとして使用する場合

今までは、テンプレートの記述はscriptタグで行ったが、これを直接文字列で記述することも可能である。

  var data = {
    name: 'Jack'
  };
  $.templates('testTmpl', '<label>Name:</label>{{:name}}');

  var html = $.render.testTmpl(data);
  $('#result').append(html);

templates関数にテンプレート名とテンプレートを指定することでテンプレートを登録できる。登録したテンプレートは、テンプレート名を指定することで利用することができる。

<p>Name:Jack</p>

テンプレートの遅延読み込み

文字をテンプレートとして、利用できることを利用してテンプレートの遅延読み込みができることを意味する。

たとえば次のような、テキストファイルが存在する。

test.txt

<label> Name:</label> {{:name}}

これを以下のような、コードでtext.txtをテンプレートとして遅延読み込みができる。

<script type="text/javascript">
$(function () {
  var data = {
    name: 'Jack'
  };
  $.get('./test.txt', function(value) {
    console.log(value);
    var testTmp = $.templates(value);
    var html = testTmp.render(data);
    $('#result').append(html);
  });
}); 
</script>

テンプレート内で別のテンプレートを使用する場合

テンプレート内でincludeタグを利用する事で別のテンプレートを使用する事が可能である。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"  type="text/javascript"></script>
<script src="./jsrender.min.js" type="text/javascript"></script>

<script id="peopleTemplate" type="text/x-jsrender">
  <div>
    {{:name}} lives in {{include tmpl="#addressTemplate"/}}
  </div>
</script>

<script id="addressTemplate" type="text/x-jsrender">
    <b>{{>address.city}}</b>
</script>

<script type="text/javascript">
$(function () {
  var data = [
    {
      "name": "Pete",
      "address": {
        "city": "Seattle"
      }
    },
    {
      "name": "Heidi",
      "address": {
        "city": "Sidney"
      }
    }
  ];
  var html = $('#peopleTemplate').render(data);
  console.log(html);
  $('#result').append(html);
}); 
</script>
</head>
<body>
  <div id="result"></div>
</body>
</html>

この時のHTMLの出力は次のようになる

 <div>
    Pete lives in 
    <b>Seattle</b>

  </div>

  <div>
    Heidi lives in 
    <b>Sidney</b>

  </div>

includeタグはtmpl属性で指定したテンプレートの内容に置き換わっていることが確認できる。
このincludeを利用する事で、テンプレートの共通化を行える。

分岐の例

テンプレート中で{{if}}}タグを利用することでテンプレートを分岐することができる。
これは必要に応じて{{else}}タグも利用できる。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"  type="text/javascript"></script>
<script src="./jsrender.min.js" type="text/javascript"></script>

<script id="theTmpl" type="text/x-jsrender">
  {{if displayType == 0}}
    <p>Name: {{:name}} </p>
  {{else displayType == 1}}
    <p>Name: {{:azana}} </p>
  {{else}}
    <p>Name: {{:nickName}} </p>
  {{/if}}
</script>

<script type="text/javascript">
$(function () {
  var data = [
    {
      "name": "Kannu",
      "azana": "Unchou",
      "nickName": "Hige",
      "displayType": 0
    },{
      "name": "Chouhi",
      "azana": "Ekitoku",
      "nickName": "Haruhi",
      "displayType": 1
    },
    {
      "name": "Ryubiu",
      "azana": "Gentoku",
      "nickName": "Kotei",
      "displayType": 2
    },
    {
      "name": "ShokatuRyou",
      "azana": "Koumei",
      "nickName": "awawa",
      "displayType": 3
    }
  ];
  var html = $('#theTmpl').render(data);
  console.log(html);
  $('#result').append(html);
}); 
</script>
</head>
<body>
  <div id="result"></div>
</body>
</html>

ここで作成されるHTMLの結果は以下のようになる。data.displayTypeにより表示される名前が切り替わっていることが確認できる。

    <p>Name: Kannu </p>
    <p>Name: Ekitoku </p>
    <p>Name: Kotei </p>
    <p>Name: awawa </p>

テンプレートに渡すデータのエスケープ処理

テンプレートに渡すデータ中の文字にHTMLのタグが存在する場合がある。このデータ中のタグをエスケープ処理を行うには、{{>データ名}}を用いる。

以下のサンプルで、エスケープ処理を行わない場合と、行った場合でどのような違いが生じるか確認する。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"  type="text/javascript"></script>
<script src="./jsrender.min.js" type="text/javascript"></script>

<script id="theTmpl" type="text/x-jsrender">
  <p>{{:description}}</p>
  <p>{{>description}}</p>
</script>

<script type="text/javascript">
$(function () {
  var data = {description: "A <b>very nice</b> appartment"};
  var html = $('#theTmpl').render(data);
  console.log(html);
  $('#result').append(html);
}); 
</script>
</head>
<body>
  <div id="result"></div>
</body>
</html>

出力結果は以下の通りとなり、{{:}}の場合は、渡された文字データがそのまま出力されているが、{{>}}でエスケープ処理が適切になされることが確認できる。

  <p>A <b>very nice</b> appartment</p>
  <p>A <b>very nice</b> appartment</p>

まとめ

JsRenderを用いることで、JavaScriptのテンプレートを記述することができ、ビューとロジックの分離を容易に行うことができる。

JsViews

JsViewsはJsRenderとJsObservableと呼ばれるライブラリを用いてテンプレートとデータのリンクを行うことができる。
これにより、データに変更があった場合に描画の自動更新をすることが可能になる。

実装のサンプルとJsViewsの機能

JsViewsを利用した実装のサンプルを用いてJsViewsの機能を紹介する
JsViewsは、JsRenderと同じページからダウンロードできる。

data-linked tagの例

ここではdata-linked タグの使用例を見てみよう。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"  type="text/javascript"></script>
<script src="./jsviews.min.js" type="text/javascript"></script>
<script id="teamTemplate" type="text/x-jsrender">

<div class="buttons">
  <button id="add">Add</button>
  <button id="update">Update</button>
</div>
<ol>
  {^{for members}}
    <li>
      {^{:name}} 
      {^{:age}}
      <img class="remove" src="http://www.jsviews.com/resources/images/close.png" />
    </li>
  {{/for}}
</ol>

</script>

<script type="text/javascript">
$(function () {
  var team = {
    members: [
      {name: "Robert", age:10},
      {name: "Sarah", age:12}
    ]
  };
  var cnt = 1;
  $.templates("#teamTemplate").link("#team", team)
    .on("click", ".remove", function() {
      var view = $.view(this);
      $.observable(team.members).remove(view.index);
    })
    .on("click", "#add", function() {
      $.observable(team.members).insert(0, {name: "new" + cnt++, age:1});
    })
    .on("click", "#update", function() {
      for(var i = 0; i < team.members.length;++i) {
        $.observable(team.members[i]).setProperty("age", team.members[i].age + 1);
      }
    });
}); 
</script>
</head>
<body>
  <div id="team"></div>
</body>
</html>

出力例:
無題.png

このプログラムを実際動作させると、Addボタンでアイテムの追加、xボタンでアイテムの削除、Updateボタンで年齢の更新が行われることがわかるだろう。つまり、ある種の操作により、表示の内容が更新されていることが確認できる。

どのように作っているかを説明する。
まずは、テンプレートの部分を見てみる。

<script id="teamTemplate" type="text/x-jsrender">

<div class="buttons">
  <button id="add">Add</button>
  <button id="update">Update</button>
</div>
<ol>
  {^{for members}}
    <li>
      {^{:name}} 
      {^{:age}}
      <img class="remove" src="http://www.jsviews.com/resources/images/close.png" />
    </li>
  {{/for}}
</ol>

</script>

JsRenderの場合、{{for members}}や{{:name}}となっていたものが、{^{for members}}や{^{:name}}になっているのがわかるだろう。「^」を使用することでデータがリンクされていることを示している。

次にデータとテンプレートを関連付けているところを見てみよう。

  $.templates("#teamTemplate").link("#team", team)
    .on("click", ".remove", function() {
      var view = $.view(this);
      $.observable(team.members).remove(view.index);
    })
    .on("click", "#add", function() {
      $.observable(team.members).insert(0, {name: "new" + cnt++, age:1});
    })
    .on("click", "#update", function() {
      for(var i = 0; i < team.members.length;++i) {
        $.observable(team.members[i]).setProperty("age", team.members[i].age + 1);
      }
    });

「#tempTemplate」で指定したテンプレートとオブジェクト「team」を関連付けて「#team」に出力している。
.onで続くものは、どのようなイベントで、このテンプレートの内容が更新されるかを記述している。
更新する場合、追加には「\$.observable.indert」,削除には「\$.observable.remove」,特定のプロパティの更新には「\$.observable.setProperty」を使用する。

その他observableの機能はJsObservableのAPIを参照するとよい。
http://www.jsviews.com/#jsoapi

data-link属性の例

ここでは data-link属性を用いた例を紹介する。
INPUTなどにdata-link属性をつけることで、INPUTで入力した内容で、data-linkで指定したデータのプロパティの変更を行うことができるようになる。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"  type="text/javascript"></script>
<script src="./jsviews.min.js" type="text/javascript"></script>
<script id="theTmpl" type="text/x-jsrender">
<input data-link="name trigger=true"/>-
<input data-link="lastName trigger=true"/>
<br>
{^{:name}}-{^{:lastName}}
</script>

<script type="text/javascript">
$(function () {
  var data = {
    "name": "Jack",
    "lastName": "Hanma"
  };

  var template = $.templates("#theTmpl");
  template.link("#result", data);

  $("#check").click(function(){
    alert(JSON.stringify(data));
  });
}); 
</script>
</head>
<body>
  <div id="result"></div>
  <button id="check">check</button>
</body>
</html>

無題.png

このサンプルを実行すると、INPUTの内容を変更すると、表示しているデータはもちろん、データとして渡したJSONの内容も変更されていることが確認できる。

まとめ

JsViewsを用いてテンプレートとデータを関連付けを行うと、画面の入力によりデータを容易に更新したり、Observerを使用してデータを更新することで画面の更新も容易に行える。

カスタムタグ

JsRenderとJsViewsの項目で{{for}}や{{else}}など様々なタグを紹介した。
JsViewsを使用することにより、開発者が任意のタグを作成することが可能だ。
この方法は下記に記述してある。
http://www.jsviews.com/#customtagsapi

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です