Backbones Dokumentation besagt:
Die events -Eigenschaft kann auch als Funktion definiert werden, die einen Ereignis-Hash zurückgibt, um die programmgesteuerte Definition Ihrer Ereignisse zu erleichtern und sie von übergeordneten Ansichten zu erben.
Wie erben Sie die View-Events eines Elternteils und erweitern Sie diese?
var ParentView = Backbone.View.extend({
events: {
'click': 'onclick'
}
});
var ChildView = ParentView.extend({
events: function(){
????
}
});
Ein Weg ist:
var ChildView = ParentView.extend({
events: function(){
return _.extend({},ParentView.prototype.events,{
'click' : 'onclickChild'
});
}
});
Ein anderes wäre:
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'
}
});
Um zu prüfen, ob Ereignisse eine Funktion oder ein Objekt sind
var ChildView = ParentView.extend({
events: function(){
var parentEvents = ParentView.prototype.events;
if(_.isFunction(parentEvents)){
parentEvents = parentEvents();
}
return _.extend({},parentEvents,{
'click' : 'onclickChild'
});
}
});
Die Antwort der Soldaten ist gut. Wenn Sie es weiter vereinfachen, können Sie Folgendes tun
var ChildView = ParentView.extend({
initialize: function(){
_.extend(this.events, ParentView.prototype.events);
}
});
Dann definieren Sie einfach Ihre Ereignisse in einer der beiden Klassen auf typische Weise.
Sie können auch die Methode defaults
verwenden, um zu vermeiden, dass das leere Objekt {}
erstellt wird.
var ChildView = ParentView.extend({
events: function(){
return _.defaults({
'click' : 'onclickChild'
}, ParentView.prototype.events);
}
});
Wenn Sie CoffeeScript verwenden und eine Funktion auf events
setzen, können Sie super
verwenden.
class ParentView extends Backbone.View
events: ->
'foo' : 'doSomething'
class ChildView extends ParentView
events: ->
_.extend {}, super,
'bar' : 'doOtherThing'
Wäre es nicht einfacher, aus Backbone.View einen spezialisierten Basiskonstruktor zu erstellen, der die Vererbung von Ereignissen in der Hierarchie übernimmt.
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
}
Dies ermöglicht es uns, den Ereignishash in der Hierarchie nach unten zu reduzieren (zusammenzuführen), wenn wir eine neue Unterklasse (untergeordneter Konstruktor) erstellen, indem Sie die neu definierte Erweiterungsfunktion verwenden.
# 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: ....
}
Durch das Erstellen einer spezialisierten Ansicht: BaseView, die die Erweiterungsfunktion neu definiert, können Unteransichten (wie AppView, SectionView) erstellt werden, die die deklarierten Ereignisse ihrer übergeordneten Ansicht erben möchten, indem sie einfach von BaseView oder einer ihrer Ableitungen erweitert werden.
Wir vermeiden die programmatische Definition unserer Ereignisfunktionen in unseren Unteransichten, die in den meisten Fällen explizit auf den übergeordneten Konstruktor verweisen müssen.
Das würde auch funktionieren:
class ParentView extends Backbone.View
events: ->
'foo' : 'doSomething'
class ChildView extends ParentView
events: ->
_.extend({}, _.result(_super::, 'events') || {},
'bar' : 'doOtherThing')
Die Verwendung von straight super
hat für mich nicht funktioniert, entweder wurde manuell die ParentView
oder die geerbte Klasse angegeben.
Zugriff auf die _super
var, die in jedem Kaffeeskript verfügbar ist Class … extends …
// 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 */ }
});
Kurze Version von @ soldier.moths letztem Vorschlag:
var ChildView = ParentView.extend({
events: function(){
return _.extend({}, _.result(ParentView.prototype, 'events') || {}, {
'click' : 'onclickChild'
});
}
});
Ich habe in diesem Artikel eine interessantere Lösung gefunden.
Die Verwendung von super des Backbones und hasOwnProperty von ECMAScript. Das zweite seiner progressiven Beispiele wirkt wie ein Zauber. Hier ist ein bisschen ein Code:
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);
},
...
});
Sie können dies auch für ui und attributes tun.
In diesem Beispiel werden die von einer Funktion festgelegten Eigenschaften nicht berücksichtigt. In diesem Fall bietet der Autor des Artikels jedoch eine Lösung.
Für Backbone-Version 1.2.3 funktioniert __super__
gut und kann sogar verkettet werden. Z.B.:
// 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",
}
// ...
});
... was in A_View.js
zu Folgendem führt:
events: {
"click .a_foo": "a_bar",
"click .b_foo": "b_bar",
"click .c_foo": "c_bar",
}
Wenn Sie sicher sind, dass für ParentView
die Ereignisse als Objekt definiert sind und Sie die Ereignisse nicht dynamisch in ChildView
definieren müssen, können Sie die Antwort von soldier.moth weiter vereinfachen, indem Sie die Funktion loslassen und _.extend
direkt verwenden:
var ParentView = Backbone.View.extend({
events: {
'click': 'onclick'
}
});
var ChildView = ParentView.extend({
events: _.extend({}, ParentView.prototype.events, {
'click' : 'onclickChild'
})
});
Wow, viele Antworten hier, aber ich dachte, ich würde noch mehr anbieten. Wenn Sie die BackSupport-Bibliothek verwenden, wird extend2
angeboten. Wenn Sie extend2
verwenden, sorgt es automatisch für das Zusammenführen von events
(sowie defaults
und ähnlichen Eigenschaften).
Hier ein kurzes Beispiel:
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
Um dies vollständig in der übergeordneten Klasse zu tun und einen funktionsbasierten Ereignishash in der untergeordneten Klasse zu unterstützen, können Kinder die Vererbung agnostisch machen (das Kind muss MyView.prototype.initialize
aufrufen, wenn es initialize
überschreibt):
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);
};
}
});
Diese CoffeeScript-Lösung hat für mich funktioniert (und berücksichtigt @ soldier.moths Vorschlag):
class ParentView extends Backbone.View
events: ->
'foo' : 'doSomething'
class ChildView extends ParentView
events: ->
_.extend({}, _.result(ParentView.prototype, 'events') || {},
'bar' : 'doOtherThing')
Ein Muster, das ich so sehr mag, ist das Ändern des Konstruktors und das Hinzufügen zusätzlicher Funktionen:
// 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'
});
}
});
Ich bevorzuge diese Methode, weil Sie nicht die übergeordnete Variable identifizieren müssen, um sie zu ändern. Ich verwende dieselbe Logik für attributes
und defaults
.