たくさんの Backbone.js ビューを作成しました。各ビューには関連する要素があります(view.el
)。
ビューのコンテキスト外でページ上の要素が与えられた場合、その要素のビューを取得するための最良の方法は何でしょうか?
たとえば、あるイベントがページ上の要素の束に影響を与え、影響を受ける要素に関連付けられているすべてのビューでメソッドを呼び出したいとします。
1つの方法は、要素のデータにビューを割り当てることですが、もっと賢いものを見逃したのではないかと思います。
var myview = BackBone.View.extend({
initialize: function(options) {
$(this.el).data('view', this);
...
}
});
(私はjQuery 1.5でBackboneを使用しています。)
このためのjQueryプラグインを作成しました。 .data()
メソッドも使用します。
バックボーンビューをラップ/プロキシしました setElement
メソッドを使用して、必要なデータをビューの_$el
_プロパティにアタッチしました。
登録は舞台裏のように行われます:
_$(myViewsEl).backboneView(myView);
_
プラグインは、必要なデータエントリを持つ要素、つまり関連するビューを持つDOM要素が見つかるまで、DOM階層を( .closest()
を使用して)トラバースします。
_var nearestView = $(e.target).backboneView();
_
さらに、取得するバックボーンビューのタイプを指定して、一致するタイプのインスタンスが見つかるまで階層を上に続けることができます。
_var nearestButtonView = $(e.target).backboneView(ButtonView);
_
見つけることができます ここ 。
ここではメモリリークが発生していないと私が正しく考えていることを願っています。 'unlink'は、setElement
が2回目に呼び出された場合に実行され、ビューの要素を削除すると、デフォルトで .remove()
が呼び出され、すべてのデータも破棄されます。 。考え方が違う場合はお知らせください。
_(function($) {
// Proxy the original Backbone.View setElement method:
// See: http://backbonejs.org/#View-setElement
var backboneSetElementOriginal = Backbone.View.prototype.setElement;
Backbone.View.prototype.setElement = function(element) {
if (this.el != element) {
$(this.el).backboneView('unlink');
}
$(element).backboneView(this);
return backboneSetElementOriginal.apply(this, arguments);
};
// Create a custom selector to search for the presence of a 'backboneView' data entry:
// This avoids a dependency on a data selector plugin...
$.expr[':'].backboneView = function(element, intStackIndex, arrProperties, arrNodeStack) {
return $(element).data('backboneView') !== undefined;
};
// Plugin internal functions:
var registerViewToElement = function($el, view) {
$el.data('backboneView', view);
};
var getClosestViewFromElement = function($el, viewType) {
var ret = null;
viewType = viewType || Backbone.View;
while ($el.length) {
$el = $el.closest(':backboneView');
ret = $el.length ? $el.data('backboneView') : null;
if (ret instanceof viewType) {
break;
}
else {
$el = $el.parent();
}
}
return ret;
};
// Extra methods:
var methods = {
unlink: function($el) {
$el.removeData('backboneView');
}
};
// Plugin:
$.fn.backboneView = function() {
var ret = this;
var args = Array.prototype.slice.call(arguments, 0);
if ($.isFunction(methods[args[0]])) {
methods[args[0]](this);
}
else if (args[0] && args[0] instanceof Backbone.View) {
registerViewToElement(this.first(), args[0]);
}
else {
ret = getClosestViewFromElement(this.first(), args[0]);
}
return ret;
}
})(jQuery);
_
すべてのビューはDOMイベントに登録できます。そのため、関心のある種類の要素を持つすべてのビューは、DOMイベントに登録してから、必要なことを実行するイベント応答関数を割り当てる必要があります。 DRY処理を行う必要がある場合は、ミックスイン手法を使用して関数をミックスインします。
この解決策は、最初に想像していたよりも簡単だと思います。ビューに意図したとおりの作業を行わせるだけです。
要素をキーとして使用し、1つまたは複数のビューを返すビューハッシュ(ディクショナリ)を維持できます。
すべてのビューには表示されているモデルへの参照があるため、モデルのIDをビューの関連要素に割り当てます(外部イベントによる変更の影響を受けないことを願っています)。また、モデルにそのビューへの参照があることを確認してください。次に、これらのモデルをバックボーンコレクションに保存します。
この設定では、要素に何かが発生したら、要素IDを使用して、上記で作成したBackboneコレクションから対応するモデルを取得します。このモデルは、ビュー参照を提供します。
Edのソリューション に触発されたメソッドを使用していますが、jQueryを使用する必要はありません。それは2つのことをします:
属性を設定しますdata-backbone-view
すべてのビューのルート要素。これは、デバッガーでDOMツリーを表示するときに便利です。どの要素がビューに関連付けられているかをすぐに確認できます。 CSSセレクターを使用することもできます[data-backbone-view]
バックボーンビューのルートである要素を検索します。
ビューの各ルート要素にbackboneView
プロパティを追加します。次に、DOM要素のプロパティを調べることにより、DOM要素からビューに移動することができます。
これは、デバッグ中のみオンにします。コードは次のとおりです。
var originalSetElement = Bb.View.prototype.setElement;
Bb.View.prototype.setElement = function setElement(element) {
if (this.el && this.el !== element) {
delete this.el.backboneView;
}
element.backboneView = this;
element.setAttribute("data-backbone-view", "true");
return originalSetElement.apply(this, arguments);
};