ちょっと話題の記事

[Grunt]grunt-contrib-jstでjsテンプレートを管理する。

2013.10.23

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

今回はCUIツールのGruntのプラグインgrunt-contrib-jstを使ったjsテンプレートを管理する機会があたので一連の流れをご紹介します。

アジェンダ

  • JavaScript Templates(JST)とは?
  • Grunt-contrib-jstを使ってみる

JavaScript Templates(JST)とは?

img-jst-000

テンプレート部分とデータ部分を関連づけて表示するイメージです。以下の記事がイメージしやすくとても参考になりました。

参考記事:JavaScriptテンプレートエンジンJsRender 基本のキ

それではJSTを用いて実際に表示してみたいと思います。今回はサンプルとして、Underscore.jsのテンプレートAPIを使って表示してみます。

使用したライブラリ

  • jquery.js
  • underscore.js
  • backbone.js
  • bootstrap.js
  • bootstrap.css

テンプレート

scriptタグ内にhtmlタグがマークアップしています。これがjsテンプレートになります。
<%= %>はテンプレート内に変数を埋め込む表記です。

<script type="text/template" id="uiListItemTemplate">
  <li class="item well">
    <span class="name">Name : <%= name %></span><br />
    <span class="age">Age : <%= age %></span>
  </li>
</script>

モデルを作成

データを管理するしてくれるモデルを作成します。

var itemModel  = Backbone.Model.extend({
    defaults: {
        name : 'Test User',
        age  : 20
    }
});

ビューを作成

今回は簡単な表示用のViewを作成します。8行目でunderscore.jsのメソッド_.template()でjsテンプレートとモデルが結合しています。

var listView = Backbone.View.extend({
    el : '#uiLists',
    template : $('#uiListItemTemplate').html(),
    initialize: function(options) {
        this.render();
    },
    render: function() {
         var element = _.template( this.template , this.model.attributes );
        this.$el.append(element);
    }
});

表示する

モデル:itemModelをインスタンス化して、ビュー:listViewをインスタンス化する際に渡して表示します。

(function(){
    var item =  new itemModel;
    var lists = new listView({ model: item });
}());

ソースコード表示

<!doctype html>
<html>
<head>
    <meta charset="UTF-8">
    <title>JST Sample</title>
    <link rel="stylesheet" href="assets/css/lib/bootstrap.css">
    <style type="text/css">#uiLists{margin-top: 20px; padding-right: 20px;}#uiLists li{list-style: none;}</style>
</head>
<body>

<ul id="uiLists"></ul>

<script type="text/template" id="uiListItemTemplate">
  <li class="item well">
    <span class="name">Name : <%= name %></span><br />
    <span class="age">Age : <%= age %></span>
  </li>
</script>

<script type="text/javascript" src="assets/js/lib/underscore.js"></script>
<script type="text/javascript" src="assets/js/lib/jquery.js"></script>
<script type="text/javascript" src="assets/js/lib/bootstrap.js"></script>
<script type="text/javascript" src="assets/js/lib/backbone.js"></script>

<script type="text/javascript">

var itemModel  = Backbone.Model.extend({
    defaults: {
        name : 'Test User',
        age  : 20
    }
});

var listView = Backbone.View.extend({
    el : '#uiLists',
    template : $('#uiListItemTemplate').html(),
    initialize: function(options) {
        this.render();
    },
    render: function() {
         var element = _.template( this.template , this.model.attributes );
        this.$el.append(element);
    }

});

(function(){
    var item =  new itemModel;
    var lists = new listView({ model: item });
}());

</script>

</body>
</html>

表示するためでしたので結構はしょってしまっていますが、実際にJSテンプレートを使ってレンダリングしてみました。

img-jst-001

今回は1つのページに1テンプレートでしたが、開発する際はJSテンプレート数が増えてきて管理も大変になってきます。

その問題を解決してくれるのがしてくれるのがビルドツールのGruntさんのプラグインgrunt-contrib-jstです。

Grunt-contrib-jstを使ってみる

grunt-contrib-jstは指定したフォルダ内のhtmlファイル(テンプレート)を、1つのUnderscore.jsのテンプレート用jsファイルに変換してくれます。

類似プラグインにHandlebarsテンプレート用grunt-contrib-handlebarsもあります。

package.json

img-jst-002

それでは早速つかってみたいと思います。まず作業用フォルダを作成し、package.jsonを作成します。

今回使用するGruntプラグインを記載します。

  • grunt-contrib-watch
  • grunt-contrib-jst
{
  "author": "Takashi Kiyota",
  "name": "grunt contrib jst Test",
  "version": "0.9.1",
  "devDependencies": {
    "grunt": "",
    "grunt-contrib-watch" : "",
    "grunt-contrib-jst" : ""
  }
}

Nodeパッケージをインストール

プラグインのインストールしてみます、コンソール画面から作業ファイルまで移動し以下のコマンドを入力します。

cd xxx/xxx/xxxx/作業フォルダ
npm install

Gruntfile.jsの作成

img-jst-003

プラグインのインストールが出来たら、Gruntfile.jsを作成します。

gruntに指定したフォルダ内にjsテンプレート用のhtmlを作成します。監視フォルダと出力ファイルはGruntfile.js(26行目)で指定ができます。今回は以下の指定をしています。

監視フォルダ
[assets/templates/]
出力ファイル
[assets/js/jst/template.js]
module.exports = function (grunt) {
  
    'use strict';

    grunt.initConfig({
  
        watch: {
            jst: {
                files: ['assets/templates/**/*.html'],
                tasks: ['jst:dev']
            }
        },
   
        jst: {
            options: {
                processName: function (filename) {
                    return filename.match(/templates\/(.+)\.html$/)[1];
                },
                processContent: function (src) {
                    return src.replace(/(^\s+|\s+$)/gm, '');
                },
                amd: true
            },
            dev: {
                files: {
                    'assets/js/jst/template.js': ['assets/templates/**/*.html']
                }
            }
        }
  
    });
  
    grunt.registerTask('default', ['watch']);
    grunt.registerTask('build', ['jst']);

};

jsテンプレート変換用htmlを作成

img-jst-004

[assets/templates/]内にsample.htmlを作成します。テンプレート側で表示する際は、htmlのファイル名を指定して表示することになります。(今回はsampleになります。)
注意点として、scriptタグに囲まずにそのまま記述します。

  <li class="item well">
    <span class="name">Name : <%= name %></span><br />
    <span class="age">Age : <%= age %></span>
  </li>

テンプレート出力

実際にテンプレートを出力してみます。コンソール画面に移動し作業フォルダのトップ階層に移動し、
以下コマンドを入力します。

grunt build

template.js

img-jst-005

[assets/js/jst/]内にtemplate.jsがされていれば成功です。中身をみると先ほど作成したsample.html内の中身がテンプレート用に変換されています。

this["JST"] = this["JST"] || {};

this["JST"]["sample"] = function(obj) {
obj || (obj = {});
var __t, __p = '', __e = _.escape;
with (obj) {
__p += '<li class="item well">\n<span class="name">Name : ' +
((__t = ( name )) == null ? '' : __t) +
'</span><br />\n<span class="age">Age : ' +
((__t = ( age )) == null ? '' : __t) +
'</span>\n</li>';

}
return __p
};

変換されたテンプレートファイルで表示する

1.template.jsを読み込む

Grunt-contrib-jstで変換されたテンプレートを読み込みます。

<script type="text/javascript" src="assets/js/jst/template.js"></script>

2.ビュー(View)の編集

3行目の箇所でcontrib-jst作成されたオブジェクトJST[]にテンプレート用htmlの名前を指定します。
今回はsample.htmlだったので、JST['sample']になります。
8行目のthis.template()の引数にモデルが保持しているデータを渡しています。

var listView = Backbone.View.extend({
    el : '#uiLists',
    template : JST['sample'],
    initialize: function(options) {
        this.render();
    },
    render: function() {
        var element = this.template(this.model.attributes);
        this.$el.append(element);
    }
});

ソースコード

<!doctype html>
<html>
<head>
    <meta charset="UTF-8">
    <title>JST Sample</title>
    <link rel="stylesheet" href="assets/css/lib/bootstrap.css">
    <style type="text/css">#uiLists{margin-top: 20px; padding-right: 20px;}#uiLists li{list-style: none;}</style>
</head>
<body>

<ul id="uiLists"></ul>

<script type="text/javascript" src="assets/js/lib/underscore.js"></script>
<script type="text/javascript" src="assets/js/lib/jquery.js"></script>
<script type="text/javascript" src="assets/js/lib/bootstrap.js"></script>
<script type="text/javascript" src="assets/js/lib/backbone.js"></script>
<script type="text/javascript" src="assets/js/jst/template.js"></script>

<script type="text/javascript">

var itemModel  = Backbone.Model.extend({
    defaults: {
        name : 'Test User',
        age  : 20
    }
});

var listView = Backbone.View.extend({
    el : '#uiLists',
    template : JST['sample'],
    initialize: function(options) {
        this.render();
    },
    render: function() {
        var element = this.template(this.model.attributes);
        this.$el.append(element);
    }
});

(function(){
    var item =  new itemModel;
    var lists = new listView({ model: item });
}());

</script>

</body>
</html>

まとめ

grunt-contrib-jstでJSテンプレートを外部ファイルtemplate.jsとして出力まで試してみました。自分的には以下の利点があるかと思いました。

1.ソースコード内にテンプレートを記述しなくて良い。
grunt-contrib-jstで変換されたjsファイルを読み込むだけで良い。
2.テンプレートの管理が楽チン
監視フォルダ[assets/templates/]の各htmlがテンプレートとなるため、該当するテンプレートの管理・編集が容易になります。