Edited at

Backbone.js入門 「View と Model の連携」

More than 3 years have passed since last update.


閲覧上の注意

この記事で対象としているバージョン0.5.3は結構古いので注意してください。

その他の割りと新しい情報は Backbone.js Advent Calendar 2012 などにあります。

(追記ここまで)


今日のテーマは ViewModel の連携です。


基本的なパターン

例えばブログサービスを作っているとします。

おそらく Blog というモデルや、それを表示するページのビューを作ることになると思いますが、記事の全文を表示するビューや複数の記事をリスト形式で表示するビュー、編集ビューなどなど、一つのモデルに対して複数のビューが存在するのが一般的ではないでしょうか。

このように ViewModel は基本的に 多対一 の関係になっています。

そのため、Model の属性が変更されたら、複数の View に変更を通知・反映しなければなりません(でなければユーザはシステムが不整合を起こしているように見えてしまいます)。

ここで思い出していただきたいのが、Backbone.js入門 「Events」 で取り上げた オブザーバ・パターン です。

Backbone.js入門 「Model」 で説明したように、Model の属性値が変更されると change イベントが発生するので、それをトリガーにして、Backbone.js入門 「View」 で説明した render メソッドを起動してやるようにするのが、オーソドックスなパターンです。

例題を使って説明していきましょう。

このブログサービスには (・∀・)イイネ!! ボタンがあり、それをクリックするとイイね数が増えていくという機能があるとします。

// ブログモデル

var Blog = Backbone.Model.extend({
// ...
});
blog = new Blog({text: 'foo', like: 0}); // (1)

// ブログ閲覧用のビュー
var ShowBlogView = Backbone.View.extend({
events: {
"click .likeBtn": "countUp" // (2)
},
initialize: function (options) {
_.bindAll(this, "render"); // (3)
this.model.bind("change", this.render); // (4)
this.render(); // (5)
},
render: function () {
$(this.el).html(_.template($("#ViewA-template").html(), this.model.attributes)); // (6)
},
countUp: function () {
this.model.set({like: this.model.get('like') + 1}); // (7)
}
});
var showBlogView = new ShowBlogView({el: $("#blog"), model: blog}); // (8)

<script type="text/template" id="ViewA-template">

<div>
<span class="likeBtn">(・∀・)イイネ!!</span>
<span class="likeCnt"><%= like %></span>
</div>
<div class="text"><%= text %></div>
</script>
<div id="blog"></div>

順番に説明していきましょう。

まず (1)Blog モデルのインスタンスを生成し、text を foo like を 0 に初期化しています。

そして (8)ShowBlogView ビューのインスタンスを生成し、el を設定し、model に先ほど作った Blog インスタンスを渡しています。このように model という名前でオブジェクトを渡すと、View インスタンスに model という名前で代入されます。(4) では、これを利用して、ブログの change イベントが発生したら render を起動するようにしています。Backbone.js 入門「Events」の最後で書いたように、コールバック関数として渡される render のコンテキストを束縛するために、(3)_.bindAll を使っています。そして、初期化の最後に (5) で自ら render を実行し、 (6) で DOM 操作を行なっています。

(6) の結果、 <div id='blog'></div> は次のようになっているはずです。

<div id="blog">

<div>
<span class="likeBtn">(・∀・)イイネ!!</span>
<span class="likeCnt">0</span>
</div>
<div class="text">foo</div>
</div>

ここでユーザが (・∀・)イイネ!! をクリックしました。

すると (2) で書いてあるように、countUp メソッドが呼ばれ、(7) でブログの like の値が1上昇します。この時 change:like イベントが発生し、(4) の結果、改めて render が呼ばれますが、今度はモデルの値が {text: 'foo', like: 1} になっているので、<div id='blog'></div> の中身は結果として次のように変更されます。

<div id="blog">

<div>
<span class="likeBtn">(・∀・)イイネ!!</span>
<span class="likeCnt">1</span>
</div>
<div class="text">foo</div>
</div>

(・∀・)イイネ!! をクリックしたら数字が1つ大きくなりました!

現状では、<div id="blog"></div> の中身を全て書きなおしていて無駄が多い状態ですね。これが気になる場合は、 (4) の他に this.model.bind('change:like', this.renderLikeCnt) を追加し renderLikeCnt メソッドの中で必要な箇所だけを更新するようにする、などの方法が考えられると思います。

ここから先でブログのビューが増え、他のビューによってモデルが変更されても、change イベントが発生する度に render が呼ばれて、最新のモデルの状態を使って DOM を構築し直すことができます。

オブザーバ・パターンを用いた Backbone.js プログラミングの勘所が少しは分かっていただけたでしょうか。


次回は

次回は Collection です。

それではまた明日。



  1. Backbone.js入門 Events

  2. Backbone.js入門 MVC

  3. Backbone.js入門 View

  4. Backbone.js入門 Model

  5. Backbone.js入門 ViewとModelの連携

  6. Backbone.js入門 Collection

  7. Backbone.js入門 ViewとModelとCollectionの連携

  8. Backbone.js入門 RouterとHistory