我的观点TuneBook
有几个类型为ClosedTune
的子视图。我还为每首曲调OpenTune
分别设置了完整页面浏览量。相同的事件都绑定在ClosedTune
和OpenTune
中,因此我设计了我的应用,以便它们都从共享的“抽象”视图Tune
继承。
为了让我的应用程序更具可扩展性,我希望将每个ClosedTune
的事件委派给TuneBook
,但为了可维护性,我希望使用相同的处理程序(存储在Tune
中的处理程序)TuneBook
使用(虽然它们显然需要包含在某些功能中)。
我遇到的问题是,在TuneBook
内找到正确的ClosedTune
来调用处理程序。什么是构建这个的好方法,还是有其他好的解决方案将事件委托给父视图?
注意 - 不是Backbone View: Inherit and extend events from parent的副本(关于从父类继承的子项,而我问的是DOM中父项的子节点的子项)
答案 0 :(得分:10)
在父视图中(也从Backbone.Events
扩展),我会将onEvent
绑定到DOM事件。在触发器上,它会触发一个主干事件,包括你的孩子看到的一些“id”属性(大概是某个行ID?)。
var TuneBook = Backbone.View.extend(_.extend({}, Backbone.Events, {
events: {
"click .tune .button": "clickHandler"
},
clickHandler: function (ev) {
this.trigger('buttonClick:' + ev.some_id_attr, ev);
},
}));
然后,子视图会自然地订阅与其有关的父视图事件。下面我在initialize
传递父视图以及之前在options
中使用的特殊id属性。
var ClosedTune = Backbone.View.extend({
initialize: function (options) {
options.parent.on('buttonClick:' + options.id, this.handler, this);
},
handler: function (ev) {
...
},
});
您当然也可以在Tune
或OpenTune
上设置类似的订阅者。
答案 1 :(得分:7)
以下是几种可能性。
ClosedTune
实例TuneBook
个对象
存储ClosedTune
中每个tune_book.tunes
的引用。如何填充tune_book.tunes
取决于您;既然您在TuneBook
上提到了加法方法,那就是我在下面说明的内容。
在TuneBook
事件处理程序中,使用类似事件目标的ClosedTune
属性之类的内容作为密钥,从tune_book.tunes
检索id
。然后调用Tune
或ClosedTune
处理程序。
var Tune = Backbone.View.extend({
className: "tune",
click_handler: function (event) {
event.preventDefault();
console.log(this.id + " clicked");
},
render: function () {
this.$el.html(
'<a href="" class="button">' + this.id + '</a>'
);
return this;
}
});
var ClosedTune = Tune.extend({});
var OpenTune = Tune.extend({
events: {
"click .button" : 'click_handler'
}
});
var TuneBook = Backbone.View.extend({
events: {
"click .tune .button" : 'click_handler'
},
click_handler: function (event) {
var tune = this.options.tunes[
$(event.target).closest(".tune").attr('id')
];
tune.click_handler( event );
},
add_tune: function (tune) {
this.options.tunes[tune.id] = tune;
this.$el.append(tune.render().el);
},
render: function () {
$("body").append(this.el);
return this;
}
});
var tune_book = new TuneBook({
tunes: {}
});
[1, 2, 3].forEach(function (number) {
tune_book.add_tune(new ClosedTune({
id: "closed-tune-" + number
}));
});
tune_book.render();
var open_tune = new OpenTune({
id: "open-tune-1"
});
$("body").append(open_tune.render().el);
jQuery.data()
创建ClosedTune
时,请存储对其的引用,例如this.$el.data('view_object', this)
。
在事件监听器中,检索ClosedTune
,例如$(event.target).data('view_object')
。
如果需要,您可以对ClosedTune
(TuneBook
)和OpenTune
使用相同的处理程序。
var Tune = Backbone.View.extend({
className: "tune",
initialize: function (options) {
this.$el.data('view_object', this);
},
click_handler: function (event) {
event.preventDefault();
var tune =
$(event.target).closest(".tune").data('view_object');
console.log(tune.id + " clicked");
},
render: function () {
this.$el.html(
'<a href="" class="button">' + this.id + '</a>'
);
return this;
}
});
var ClosedTune = Tune.extend({
initialize: function (options) {
this.constructor.__super__.initialize.call(this, options);
}
});
var OpenTune = Tune.extend({
events: {
"click .button" : 'click_handler'
}
});
var TuneBook = Backbone.View.extend({
events: {
"click .tune .button": Tune.prototype.click_handler
},
add_tune: function (tune) {
this.$el.append(tune.render().el);
},
render: function () {
$("body").append(this.el);
return this;
}
});
var tune_book = new TuneBook({
tunes: {}
});
[1, 2, 3].forEach(function (number) {
tune_book.add_tune(new ClosedTune({
id: "closed-tune-" + number
}));
});
tune_book.render();
var open_tune = new OpenTune({
id: "open-tune-1"
});
$("body").append(open_tune.render().el);
我考虑过选项1,但决定反对它,因为我已经在乐团中收集了一些曲调模型,并且不想要另一个我需要保持同步的对象
我想这取决于你认为需要做什么样的家务/同步,以及为什么。
(例如在TuneModel.remove()中我需要从tunebook的视图列表中删除视图...可能需要事件来执行此操作,因此仅事件解决方案开始看起来更具吸引力。)
为什么你觉得你“需要从tunebook的视图列表中删除视图”? (我不是建议你不应该这样做,只是问为什么你想要。)既然你这样做了,你认为@ ggozad的方法在这方面有何不同?
这两种技术都在ClosedTune
实例中存储TuneBook
个对象。在@ ggozad的技术中,它只是隐藏在一个抽象背后,这可能使你不那么明显。
在我的例子中,它们存储在一个普通的JS对象(tune_book.tunes
)中。在@ ggozad中,它们存储在_callbacks
使用的Backbone.Events
结构中。
添加ClosedTune
:
1
this.options.tunes[tune.id] = tune;
2
this.on('buttonClick:' + tune.id, tune.handler, tune);
如果你想摆脱ClosedTune
(假设你用tune.remove()
从文档中删除它,并且你希望视图对象完全消失),使用@ ggozad的方法将留下一个孤立的引用ClosedTune
中的tune_book._callbacks
,除非您使用我建议的方法执行相同类型的内务管理:
1
delete this.options.tunes[tune.id];
tune.remove();
2
this.off("buttonClick:" + tune.id);
tune.remove();
每个示例的第一行是可选的 - 取决于您是否要清理ClosedTune
个对象。
选项2或多或少是我现在所做的,但是(由于其他原因)我还将模型存储为视图上的数据属性。$ el,我不禁觉得必须要比在整个地方存储引用更好的方法。
嗯,最终归结为你对如何构建事物的偏好。如果您希望以更集中的方式存储视图对象,则可以将它们存储在TuneBook
实例中,而不是使用jQuery.data
。见#1:集中化。
您存储对ClosedTune
个对象的引用的方式:使用jQuery.data
,或TuneBook
中的普通对象,或_callbacks
中的TuneBook
{{1}}。
如果您因为理解的原因喜欢@ ggozad的方法,那就去吧,但这并不神奇。正如在这里介绍的那样,我不确定与#1中提出的更直接的版本相比,额外的抽象级别应该提供什么样的优势。如果有一些优势,请随时填写。
答案 2 :(得分:0)
我从article(@ dave-cadwallader评论)中获得了很好的解决方案。
扩展常规的主干事件对象并将其存储在引用vent
中:
var vent = _.extend({}, Backbone.Events);
将其传递到父视图:
var parentView = new ParentView({vent: vent});
子视图将触发一个事件:
ChildView = Backbone.View.extend({
initialize: function(options){
this.vent = options.vent;
},
myHandler: function(){
this.vent.trigger("myEvent", this.model);
}
});
父视图正在监听子事件:
ParentView = Backbone.View.extend({
initialize: function(options){
this.vent = options.vent;
this.vent.on("myEvent", this.onMyEvent);
let childView = new ChildView({vent: this.vent});
},
onMyEvent: function(){
console.log("Child event has been ");
}
});
免责声明-注意vent
对象必须注入到每个视图中,因此您将在本文中找到更好的设计模式来使用。