閲覧上の注意
この記事で対象としているバージョン0.5.3は結構古いので注意してください。
その他の割りと新しい情報は Backbone.js Advent Calendar 2012 などにあります。
(追記ここまで)
今日のテーマは View
と Model
の連携です。
##基本的なパターン
例えばブログサービスを作っているとします。
おそらく Blog
というモデルや、それを表示するページのビューを作ることになると思いますが、記事の全文を表示するビューや複数の記事をリスト形式で表示するビュー、編集ビューなどなど、一つのモデルに対して複数のビューが存在するのが一般的ではないでしょうか。
このように View
と Model
は基本的に 多対一 の関係になっています。
そのため、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
です。
それではまた明日。