BackboneのViewは、コードというよりはほとんど慣習に近い存在です ー ViewはHTMLやCSSについて何も決定付けることはなく、任意のJavaScriptテンプレーティングライブラリと合わせて使用できます。一般的なアイディアとしては、ページを全体を再描画せず、モデルが変更されたときにそれぞれが独立して更新できる、Modelに支えられたロジカルなViewとしてインターフェースを整理します。JSONオブジェクトを掘り、DOMの中にある要素を参照し、手動でHTMLを更新する代わりに、Viewのrender関数をモデルの"change"イベントに結びつけることができます ー そうすると、UIの中のモデルデータを表示しているすべての箇所は、いつでも即座に最新になります。
Backbone.View.extend(properties, [classProperties])Viewはまずカスタムビュークラスを作ることから始めます。render関数をオーバーライドし、宣言するイベントを指定し、tagNameやclassName、Viewのルート要素のidもおそらく指定するとよいでしょう。
var DocumentRow = Backbone.View.extend({
tagName: "li",
className: "document-row",
events: {
"click .icon": "open",
"click .button.edit": "openEditDialog",
"click .button.delete": "destroy"
},
render: function() {
...
}
});
new View([options])新しいビューを作成するときに与えたオプションは、後から参照するためthis.optionsとしてViewにアタッチされます。いくつかの特殊なオプション model、collection、el、id、className、tagName、そしてattributes があり、それらが渡された場合はViewのプロパティとして直接アタッチされます。 initialize 関数がViewに定義されている場合、それはViewが最初に作られたときに呼ばれます。DOMの中に 既に 存在する要素を参照して、Viewを作成したい場合は new View({el: existingElement}) のようにオプションとして渡します。
var doc = Documents.first();
new DocumentRow({
model: doc,
id: "document-row-" + doc.id
});
view.elすべてのViewは、それが既にページに挿入されているかそうでないかに関わらず、常に何らかのDOM要素を所持しています( el プロパティ)。こうすることで、UIの描画を効率良く行うため、できるだけ少ないリフローとリペイントで、Viewはいつでもレンダリングでき、一度にすべてをDOMへ挿入するようになっています。ViewにtagName、className、idそしてattributesプロパティが指定されていれば、それらからthis.elが作られます。そうでない場合は、 el は空のdivになります。
var ItemView = Backbone.View.extend({
tagName: 'li'
});
var BodyView = Backbone.View.extend({
el: 'body'
});
var item = new ItemView();
var body = new BodyView();
alert(item.el + ' ' + body.el);
view.$elViewを示す要素のjQuery(またはZepto)オブジェクトがキャッシュされています。都度、DOM要素をオブジェクトでラップする代わりとして、便利に参照できます。
view.$el.show();
listView.$el.append(itemView.el);
view.setElement(element)BackboneのViewを違うDOM要素に適用したい場合、 setElement を使用します。このメソッドは$elの参照をキャッシュして、Viewがもつイベントの委譲を古い要素から新しい要素に移動させます。
view.attrributesViewのelのDOM要素としての属性(id、class、data-propertiesなど)にセットされる、属性のハッシュまたはそのようなハッシュを返す関数です。
view.$(selector)jQueryまたはZeptoがページに読み込まれている場合、それぞれのViewは自身の要素の中をスコープとしてクエリーを実行する $ 関数を持ちます。このスコープにあるjQuery関数を使う場合、特定の要素をリストから取ってくるためにモデルのIDをクエリーの一部分として使う必要はなく、HTMLのクラス属性により強く依存するようになります。これは、view.$el.find(selector)の実行と等価です。
ui.Chapter = Backbone.View.extend({
serialize : function() {
return {
title: this.$(".title").text(),
start: this.$(".start-page").text(),
end: this.$(".end-page").text()
};
}
});
view.render()デフォルトの実装の render は何もしません。ViewのテンプレートをModelのデータからレンダリングし、新しいHTMLでthis.elを更新するような、独自のコードで上書きします。よい慣習として、コールチェーンを有効にするため render の最後でreturn thisします。
var Bookmark = Backbone.View.extend({
render: function() {
$(this.el).html(this.template(this.model.toJSON()));
return this;
}
});
Backboneは、あなたが好むHTMLテンプレーティングの方法にとらわれることなく関知しません。あなたの render 関数は、HTML文字列を置換することも、document.createElementでDOMツリーを作成することも同様に行うことができます。しかし、我々は素晴らしいJavaScriptテンプレーティングライブラリを選ぶことをおすすめします。Mustache.jsやHaml-js、そしてEco、これらはすべて良い代替手段と言えます。そのように言うのも、ページにはUnderscore.jsが既にあるため_.templateを利用することができ、XSS対策のサニタイズが済んだデータを挿入する場合は、素晴らしい選択になるからです。
テンプレーティングによる方略がどのようなものであっても結局、JavaScriptでHTML文字列を配置する必要がなければ、それは良いことです。DocumentCloudでは、core.jsのアセットパッケージの一部として、/app/viewsに格納されたJavaScriptテンプレートをパッケージするのにJammitを利用しています。
view.remove()DOMからViewを削除するために便利な関数です。$(view.el).remove()を呼び出すのと等価です。
view.make(tagName, [attributes], [content])オプションの属性とHTMLコンテントを伴って、与えられた型( tagName )のDOM要素を作成するのに便利な関数です。内部的にview.elを初期化するのに使用しています。
var view = new Backbone.View;
var el = view.make("b", {"class": "bold"}, "Bold! ");
$("#make-demo").append(el);
delegateEvents([events])Viewの中のDOMイベントに対して、コールバックの宣言を提供するのにjQueryのdelegateを使用しています。 events ハッシュが直接渡されていない場合、this.eventsがソースとして使われます。イベントは、{"event selector": "callback"}という形式で記述します。コールバックは、Viewにあるメソッド名、または関数本体が直接指定されるかもしれません。セレクタを省略すると、イベントはViewのルート要素( this.el )に結びつけられます。デフォルトで、delegateEventsはViewのコンストラクタの中で呼び出され、単純にeventsハッシュを使用している場合は、すべてのDOMイベントが常にすでに接続されていることになり、自身でこの関数を呼び出す必要はありません。
eventsプロパティは events ハッシュを返す関数として定義することもでき、プログラム的にイベントを定義するほかに、親のViewのイベントを継承することも簡単にしてくれます。
delegateEvents を使用することはrender中に、jQueryを使って手動で子要素にイベントを結びつけることよりも、多くのアドバンテージを提供します。すべてのアタッチされているコールバックは、コールバックが呼び出されたとき、Viewオブジェクトを参照し続けるために、jQueryに引き渡される前にViewに結びつけられています。異なったeventsハッシュを伴って delegateEvents がもう一度実行されるようなときには、すべてのコールバックを削除し、新しく委譲を行います ー 別のモードで異なった動作を必要とする時があるViewにとって有用です。
文書内の検索結果を表示するViewは、このような見た目になるかもしれません:
var DocumentView = Backbone.View.extend({
events: {
"dblclick" : "open",
"click .icon.doc" : "select",
"contextmenu .icon.doc" : "showMenu",
"click .show_notes" : "toggleNotes",
"click .title .lock" : "editAccessLevel",
"mouseover .title .date" : "showTooltip"
},
render: function() {
$(this.el).html(this.template(this.model.toJSON()));
return this;
},
open: function() {
window.open(this.model.get("viewer_url"));
},
select: function() {
this.model.set({selected: true});
},
...
});
undelegateEvents()Viewに委譲されたすべてのイベントを削除します。Viewを一時的にDOMから削除または無効化するときに有用です。