主干视图: 从父事件继承和扩展事件

Backbone 的文档指出:

The events property may also be defined as a function that returns an events hash, to make it easier to programmatically define your events, as well as inherit them from parent views.

如何继承父视图事件并扩展它们?

Parent View

var ParentView = Backbone.View.extend({
events: {
'click': 'onclick'
}
});

Child View

var ChildView = ParentView.extend({
events: function(){
????
}
});
50048 次浏览

一种方法是:

var ChildView = ParentView.extend({
events: function(){
return _.extend({},ParentView.prototype.events,{
'click' : 'onclickChild'
});
}
});

另一种可能是:

var ParentView = Backbone.View.extend({
originalEvents: {
'click': 'onclick'
},
//Override this event hash in
//a child view
additionalEvents: {
},
events : function() {
return _.extend({},this.originalEvents,this.additionalEvents);
}
});


var ChildView = ParentView.extend({
additionalEvents: {
'click' : ' onclickChild'
}
});

检查 Events 是函数还是对象

var ChildView = ParentView.extend({
events: function(){
var parentEvents = ParentView.prototype.events;
if(_.isFunction(parentEvents)){
parentEvents = parentEvents();
}
return _.extend({},parentEvents,{
'click' : 'onclickChild'
});
}
});

蛾子的答案是一个很好的答案

var ChildView = ParentView.extend({
initialize: function(){
_.extend(this.events, ParentView.prototype.events);
}
});

然后用典型的方法定义两个类中的事件。

您还可以使用 defaults方法来避免创建空对象 {}

var ChildView = ParentView.extend({
events: function(){
return _.defaults({
'click' : 'onclickChild'
}, ParentView.prototype.events);
}
});

简单来说就是“士兵. 蛾子”的最后一条建议:

var ChildView = ParentView.extend({
events: function(){
return _.extend({}, _.result(ParentView.prototype, 'events') || {}, {
'click' : 'onclickChild'
});
}
});

如果使用 CoffeeScript 并将函数设置为 events,则可以使用 super

class ParentView extends Backbone.View
events: ->
'foo' : 'doSomething'


class ChildView extends ParentView
events: ->
_.extend {}, super,
'bar' : 'doOtherThing'

这个 CoffeeScript 解决方案对我很有效(并且考虑到了这位士兵的建议) :

class ParentView extends Backbone.View
events: ->
'foo' : 'doSomething'


class ChildView extends ParentView
events: ->
_.extend({}, _.result(ParentView.prototype, 'events') || {},
'bar' : 'doOtherThing')

这也会奏效:

class ParentView extends Backbone.View
events: ->
'foo' : 'doSomething'


class ChildView extends ParentView
events: ->
_.extend({}, _.result(_super::, 'events') || {},
'bar' : 'doOtherThing')

直接使用 super对我来说不起作用,要么是手动指定 ParentView,要么是继承的类。

访问 _super变量,该变量在任何咖啡脚本 Class … extends …中都可用

如果你确定 ParentView有定义为 object 的事件,而且你不需要在 ChildView中动态定义事件,那么通过去掉函数并直接使用 _.extend,就可以进一步简化二等兵的回答:

var ParentView = Backbone.View.extend({
events: {
'click': 'onclick'
}
});


var ChildView = ParentView.extend({
events: _.extend({}, ParentView.prototype.events, {
'click' : 'onclickChild'
})
});

我喜欢的一种模式是修改构造函数并添加一些额外的功能:

// App View
var AppView = Backbone.View.extend({


constructor: function(){
this.events = _.result(this, 'events', {});
Backbone.View.apply(this, arguments);
},


_superEvents: function(events){
var sooper = _.result(this.constructor.__super__, 'events', {});
return _.extend({}, sooper, events);
}


});


// Parent View
var ParentView = AppView.extend({


events: {
'click': 'onclick'
}


});


// Child View
var ChildView = ParentView.extend({


events: function(){
return this._superEvents({
'click' : 'onclickChild'
});
}


});

I prefer this method because you do not have to identify the parent -one less variable to change. I use the same logic for attributes and defaults.

// ModalView.js
var ModalView = Backbone.View.extend({
events: {
'click .close-button': 'closeButtonClicked'
},
closeButtonClicked: function() { /* Whatever */ }
// Other stuff that the modal does
});


ModalView.extend = function(child) {
var view = Backbone.View.extend.apply(this, arguments);
view.prototype.events = _.extend({}, this.prototype.events, child.events);
return view;
};


// MessageModalView.js
var MessageModalView = ModalView.extend({
events: {
'click .share': 'shareButtonClicked'
},
shareButtonClicked: function() { /* Whatever */ }
});


// ChatModalView.js
var ChatModalView = ModalView.extend({
events: {
'click .send-button': 'sendButtonClicked'
},
sendButtonClicked: function() { /* Whatever */ }
});

Http://danhough.com/blog/backbone-view-inheritance/

从 Backbone 创建专用的基础构造函数不是更容易吗。处理层次结构上事件继承的视图。

BaseView = Backbone.View.extend {
# your prototype defaults
},
{
# redefine the 'extend' function as decorated function of Backbone.View
extend: (protoProps, staticProps) ->
parent = this


# we have access to the parent constructor as 'this' so we don't need
# to mess around with the instance context when dealing with solutions
# where the constructor has already been created - we won't need to
# make calls with the likes of the following:
#    this.constructor.__super__.events
inheritedEvents = _.extend {},
(parent.prototype.events ?= {}),
(protoProps.events ?= {})


protoProps.events = inheritedEvents
view = Backbone.View.extend.apply parent, arguments


return view
}

This allows us to reduce(merge) the events hash down the hierarchy whenever we create a new 'subclass'(child constructor) by using the redefined extend function.

# AppView is a child constructor created by the redefined extend function
# found in BaseView.extend.
AppView = BaseView.extend {
events: {
'click #app-main': 'clickAppMain'
}
}


# SectionView, in turn inherits from AppView, and will have a reduced/merged
# events hash. AppView.prototype.events = {'click #app-main': ...., 'click #section-main': ... }
SectionView = AppView.extend {
events: {
'click #section-main': 'clickSectionMain'
}
}


# instantiated views still keep the prototype chain, nothing has changed
# sectionView instanceof SectionView => true
# sectionView instanceof AppView => true
# sectionView instanceof BaseView => true
# sectionView instanceof Backbone.View => also true, redefining 'extend' does not break the prototype chain.
sectionView = new SectionView {
el: ....
model: ....
}

By creating a specialized view: BaseView that redefines the extend function, we can have subviews(like AppView, SectionView) that want to inherit their parent view's declared events simply do so by extending from BaseView or one of its derivatives.

我们避免了在子视图中以编程方式定义事件函数的需要,这在大多数情况下需要显式地引用父构造函数。

Wow, lots of answers here but I thought I'd offer one more. If you use the BackSupport library, it offers extend2. If you use extend2 it automatically takes care of merging events (as well as defaults and similar properties) for you.

这里有一个简单的例子:

var Parent = BackSupport.View.extend({
events: {
change: '_handleChange'
}
});
var Child = parent.extend2({
events: {
click: '_handleClick'
}
});
Child.prototype.events.change // exists
Child.prototype.events.click // exists

Https://github.com/machineghost/backsupport

For Backbone version 1.2.3, __super__ works fine, and may even be chained. E.g.:

// A_View.js
var a_view = B_View.extend({
// ...
events: function(){
return _.extend({}, a_view.__super__.events.call(this), { // Function - call it
"click .a_foo": "a_bar",
});
}
// ...
});


// B_View.js
var b_view = C_View.extend({
// ...
events: function(){
return _.extend({}, b_view.__super__.events, { // Object refence
"click .b_foo": "b_bar",
});
}
// ...
});


// C_View.js
var c_view = Backbone.View.extend({
// ...
events: {
"click .c_foo": "c_bar",
}
// ...
});

... 在 A_View.js中,结果是:

events: {
"click .a_foo": "a_bar",
"click .b_foo": "b_bar",
"click .c_foo": "c_bar",
}

我在这个 文章中找到了一个更有趣的解决方案

它使用主干的 好极了和 ECMAScript 的 hasOwnProperty。它的第二个进步主义例子非常有效。这里有一点暗号:

var ModalView = Backbone.View.extend({
constructor: function() {
var prototype = this.constructor.prototype;


this.events = {};
this.defaultOptions = {};
this.className = "";


while (prototype) {
if (prototype.hasOwnProperty("events")) {
_.defaults(this.events, prototype.events);
}
if (prototype.hasOwnProperty("defaultOptions")) {
_.defaults(this.defaultOptions, prototype.defaultOptions);
}
if (prototype.hasOwnProperty("className")) {
this.className += " " + prototype.className;
}
prototype = prototype.constructor.__super__;
}


Backbone.View.apply(this, arguments);
},
...
});

对于 是的属性也可以这样做。

这个示例并不考虑函数所设置的属性,但是本文的作者在这种情况下提供了一个解决方案。

要完全在父类中完成这项工作,并在子类中支持基于函数的事件散列,这样子类就可以不知道继承(如果子类覆盖了 initialize,那么子类就必须调用 MyView.prototype.initialize) :

var MyView = Backbone.View.extend({
events: { /* ... */ },


initialize: function(settings)
{
var origChildEvents = this.events;
this.events = function() {
var childEvents = origChildEvents;
if(_.isFunction(childEvents))
childEvents = childEvents.call(this);
return _.extend({}, MyView.prototype.events, childEvents);
};
}
});