Do You PHP はてブロ

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

JsViewsチュートリアル - データリンクしたオブジェクトのメソッドを呼び出す

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

今回やること

JsViewsでデータリンクしたオブジェクトのメソッドを呼び出す。この際、データリンクした値が変更された場合にメソッドの戻り値を再評価させてみる。

コード

<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">
// リンクされたオブジェクトのメソッドを呼び出す
$(function() {
    var data = {
        a: 'foo',
        b: 'bar',
        unlinkedFunction: function() {
            return 'hoge';
        },
        linkedFunction: function(a, b) {
            return a === b;
        },
        notLinkedFunction: function() {
            return this.a === this.b;
        },
        notLinkedFunctionWithDepends: function() {
            return this.a === this.b;
        }
    };

    // notLinkedFunctionWithDependsの戻り値はデータリンクした値に
    // 依存しているので、依存関係を定義する
    //
    //   [対象のメソッド].depends = [対象の変数, 依存するプロパティへのパス, ...]
    //
    // "*"は、dataオブジェクト直下のすべてのプロパティについて変化した
    // かどうかを監視する
    data.notLinkedFunctionWithDepends.depends = [data, '*'];

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

    // テンプレート"linkTemplate"と変数dataをリンク
    $.link.linkTemplate("#result", data);
});
</script>

<!-- テンプレート -->
<script id="template" type="text/x-jsrender">
<p>テキストボックスが同じ値の場合、boolの値が変化します</p>
<div>
    <p data-link="a"></p>
    <input type="text" data-link="a trigger=true"/>
</div>
<div>
    <p data-link="b"></p>
    <input type="text" data-link="b trigger=true"/>
</div>
<p>固定値:<span data-link="unlinkedFunction()"></span></p>
<p>linkedFunction(a, b):<span data-link="linkedFunction(a, b)"></span></p>
<p>notLinkedFunction():<span data-link="notLinkedFunction()"></span></p>
<p>notLinkedFunctionWithDespanends():<span data-link="notLinkedFunctionWithDepends()"></span></p>
</script>
</body>
</html>

説明

JsRenderチュートリアル - テンプレートに渡したオブジェクトのメソッドを呼び出す - Do You PHP はてなでテンプレートに渡されたオブジェクトのメソッドを呼び出してみましたが、JsViewsの場合も基本的にはそのまま記述すればOKです。ただし、データリンクした値が変更された場合にメソッドの戻り値を再評価したいかどうかJavaScript側のコードが変わってきます。

1. 戻り値を再評価しなくて良い場合

JsRenderと同様の記述方法でOKです。unlinkedFunctionメソッドはデータリンクした値と関連がありません。

<span data-link="unlinkedFunction()"></span>
2. 戻り値を再評価するが、引数でデータリンクした値を受け取っている場合

これもJsRenderと同様の記述方法でOKです。linkedFunctionメソッドはデータリンクした値を受け取っているので、「データリンクした値が変更された場合にメソッドを呼び出す」といった依存関係が自動的に定義されるためです。

<span data-link="linkedFunction(a, b)"></span>
3. 戻り値を再評価するが、引数でデータリンクした値を受け取っていない場合

たとえば

var variable = {
    a: {
        b: {
            c: 'foo'
            d: 'bar'
        },
        e: 'bar'
    },
    func: function() {
        return this.a.b.c > this.a.b.d && this.a.e !== 'hoge';
    }
};

という階層を持ったオブジェクトをデータリンクしても、JsViews(実際はJsObservable)にとってはfuncメソッドがどういう依存関係を持っているのか分からないため、戻り値が再評価されません。この場合は先ほど説明した依存関係を定義する必要があります
依存関係の定義方法ですが、定義したいオブジェクトメソッドのdependsプロパティとして、

  • 対象となるデータオブジェクトの変数(省略可)
  • 対象とするプロパティへのパス(複数可)

を配列もしくは文字列として記述します。

[対象のメソッド].depends = [対象の変数, 依存するプロパティへのパス, ...]

後者のプロパティへのパスですが、先ほど出てきたオブジェクトの場合、プロパティcへのパスは

a.b.c

また、プロパティeへのパスは

a.e

といった具合になります。また、直下のすべてのプロパティを表す"*"(アスタリスク)を指定することもできます。
ここまで踏まえた依存関係の定義例は以下のとおりになります。それぞれ、いずれの書き方でもOKです。

// variableオブジェクトのプロパティ"e"が変更された場合にのみ
// variable.funcを再評価する
variable.func.depends = [variable, 'a.e'];
variable.func.depends = [variable.a, 'e'];
variable.func.depends = 'a.e';

// variableオブジェクトのプロパティ"c"または"d"が変更された場合に
// variable.funcを再評価する
variable.func.depends = [variable, 'a.b.c', 'a.b.d'];
variable.func.depends = [variable, 'a.b.*'];
variable.func.depends = [variable.a, 'b.c', 'b.d'];
variable.func.depends = [variable.a, 'b.*'];
variable.func.depends = [variable.a.b, 'c', 'd'];
variable.func.depends = [variable.a.b, '*'];
variable.func.depends = ['a.b.c', 'a.b.d'];
variable.func.depends = ['a.b.*'];

// variableオブジェクトのすべてのプロパティについて、変更があった
// 場合にvariable.funcを再評価する
variable.func.depends = [variable, 'a.e', 'a.b.c', 'a.b.e'];
variable.func.depends = [variable, 'a.*', 'a.b.*'];
variable.func.depends = ['a.e', 'a.b.c', 'a.b.e'];
variable.func.depends = ['a.*', 'a.b.*'];

JSFiddleで動作を見る