我有一个主干应用程序,其视图结构如下所示 - 请注意,为了简洁起见,我删除了实现、模型、集合等:
NewsListView = Backbone.View.extend({
el: $('li#newspane'),
// This is what I would like to be able to do
// events: { 'filtered': 'reset' }
initialize: function() {
_.bindAll(this);
},
render: function() {
},
reset: function(){
}
});
FilterView = Backbone.View.extend({
el: $('li.filter'),
initialize: function() {
},
render: function() {
},
toggleFilter: function() {
}
});
AllView = Backbone.View.extend({
initialize: function() {
this.newsListView = new NewsListView();
this.filterView = new FilterView();
}
});
本质上,每当调用FilterView
的toggleFilter()
函数时,我想触发一个名为filtered
或类似事件的事件,然后被NewsListView
捕获,然后调用其reset()
函数。如果不将NewsListView
对象的引用传递给我的FilterView
,我不确定如何向它发送事件。有什么想法吗?
你走在正确的轨道上。听起来您需要的是全球事件调度程序。 这里有一篇不错的文章和例子:http://www.michikono.com/2012/01/11/adding-a-centralized-event-dispatcher-on-backbone-js/
您可以使用 jquery events 的现有功能和 backbone events 属性来执行此操作。
例如,不是从子视图中执行此操作:
this.trigger("yourevent", this);
请改为执行以下操作:
this.$el.trigger("yourevent", this);
然后,在作为子视图的父视图、祖父视图等的任何视图中,通过在视图的 events 对象上定义属性来侦听该视图$el上的事件:
events:{
"yourevent":"yourhandler"
}
并在该视图上定义处理程序:
yourhandler:function(subview) {
}
因此,这样,视图就不需要知道存在哪些后代视图,只需要知道它感兴趣的事件类型。 如果发起事件的视图被销毁,则无需更改上级视图的任何内容。 如果祖先视图被销毁,Backbone 将自动分离处理程序。
警告:我还没有真正尝试过这个,所以可能在某个地方有一个陷阱。
你应该看看 Backbone.Courier 插件,因为冒泡事件是一个完美的用例:
https://github.com/dgbeck/backbone.courier
我发现触发和侦听事件的最简单方法是使用 Backbone 对象本身。它已经混合了事件函数,因此您只需触发例如:
Backbone.trigger('view:eventname',{extra_thing:whatever, thing2:whatever2});
然后,在应用程序的任何其他主干视图中,您可以侦听此事件,例如:
Backbone.on('view:eventname', function(passed_obj) {
console.log(passed_obj.extra_thing);
});
我不完全确定不使用 Backbone 对象作为事件处理程序,而是创建一个单独的对象来执行此操作有什么好处,但对于快速而肮脏的工作,上述工作正常。哼!
注意:这样做的一个缺点是每个侦听器都会"听到"以这种方式触发的每个事件。不知道大O是什么,但要小心不要用很多这些东西来超载你的观点。再说一遍:这又快又脏!:)
可以用小骨干来解决.js黑客。只需修改 Backbone.Events.trigger 即可将事件传递到this.parent
if this.parent != null
所以,我想出了一个解决方案 - 创建一个扩展Backbone.Events
的对象,并将其作为参数传递给多个视图。这几乎感觉像是演员之间的信息传递,或者其他什么。无论如何 - 我将其作为答案发布,以防其他人需要快速解决方案,但我不会接受答案。这感觉很笨拙。我仍然希望看到更好的解决方案。
NewsListView = Backbone.View.extend({
el: $('li#newspane'),
// Too bad this doesn't work, it'd be really convenient
// events: { 'filtered': 'reset' }
initialize: function() {
_.bindAll(this);
// but at least this does
this.options.eventProxy.bind('filtered', this.reset);
},
render: function() {},
reset: function() {}
});
FilterView = Backbone.View.extend({
el: $('li.filter'),
initialize: function() {},
render: function() {},
toggleFilter: function() {
this.options.eventProxy.trigger('filtered');
}
});
AllView = Backbone.View.extend({
initialize: function() {
var eventProxy = {};
_.extend(eventProxy, Backbone.Events);
this.newsListView = new NewsListView({eventProxy: eventProxy});
this.filterView = new FilterView({eventProxy: eventProxy});
}
});