Les états de documentation de backbone:
La propriété des événements peut également être défini comme une fonction qui renvoie un hachage d'événements, pour le rendre plus facile de définir vos événements par programmation, ainsi que les vues hériter de parents.
Comment pouvez-vous héritez un des événements de vue des parents et de les étendre?
var ParentView = Backbone.View.extend({
events: {
'click': 'onclick'
}
});
var ChildView = ParentView.extend({
events: function(){
????
}
});
Une façon est:
var ChildView = ParentView.extend({
events: function(){
return _.extend({},ParentView.prototype.events,{
'click' : 'onclickChild'
});
}
});
Un autre serait:
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'
}
});
Pour vérifier si Events est une fonction ou un objet
var ChildView = ParentView.extend({
events: function(){
var parentEvents = ParentView.prototype.events;
if(_.isFunction(parentEvents)){
parentEvents = parentEvents();
}
return _.extend({},parentEvents,{
'click' : 'onclickChild'
});
}
});
La réponse du soldat est bonne. En simplifiant davantage, vous pouvez simplement faire ce qui suit
var ChildView = ParentView.extend({
initialize: function(){
_.extend(this.events, ParentView.prototype.events);
}
});
Ensuite, définissez simplement vos événements dans l'une ou l'autre classe.
Vous pouvez également utiliser la méthode defaults
pour éviter de créer la {}
objet vide.
var ChildView = ParentView.extend({
events: function(){
return _.defaults({
'click' : 'onclickChild'
}, ParentView.prototype.events);
}
});
Si vous utilisez CoffeeScript et définissez une fonction sur events
, vous pouvez utiliser super
.
class ParentView extends Backbone.View
events: ->
'foo' : 'doSomething'
class ChildView extends ParentView
events: ->
_.extend {}, super,
'bar' : 'doOtherThing'
Ne serait-il pas plus facile de créer un constructeur de base spécialisé à partir de Backbone.View qui gère l'héritage des événements dans la hiérarchie.
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
}
Cela nous permet de réduire (fusionner) le hachage des événements dans la hiérarchie chaque fois que nous créons une nouvelle «sous-classe» (constructeur enfant) à l'aide de la fonction d'extension redéfinie.
# 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: ....
}
En créant une vue spécialisée: BaseView qui redéfinit la fonction d’extension, nous pouvons avoir des sous-vues (telles que AppView, SectionView) qui souhaitent hériter des événements déclarés de leur vue parent simplement en procédant à une extension depuis BaseView ou l’un de ses dérivés.
Nous évitons la nécessité de définir par programme nos fonctions d'événement dans nos sous-vues, qui dans la plupart des cas doivent faire explicitement référence au constructeur parent.
Cela fonctionnerait aussi:
class ParentView extends Backbone.View
events: ->
'foo' : 'doSomething'
class ChildView extends ParentView
events: ->
_.extend({}, _.result(_super::, 'events') || {},
'bar' : 'doOtherThing')
Utiliser straight super
ne fonctionnait pas pour moi, soit spécifiait manuellement la classe ParentView
ou héritée.
Accès à la _super
var disponible dans n'importe quel coffeescript 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 */ }
});
Version courte de la dernière suggestion de @ soldier.my:
var ChildView = ParentView.extend({
events: function(){
return _.extend({}, _.result(ParentView.prototype, 'events') || {}, {
'click' : 'onclickChild'
});
}
});
J'ai trouvé des solutions plus intéressantes dans cet article article
Il utilise le super de Backbone et hasOwnProperty de ECMAScript. Le second de ses exemples progressifs fonctionne à merveille. Voici un peu un 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);
},
...
});
Vous pouvez également le faire pour ui et attributs.
Cet exemple ne prend pas en charge les propriétés définies par une fonction, mais l'auteur de l'article propose une solution dans ce cas.
Pour la version 1.2.3 de Backbone, __super__
fonctionne correctement et peut même être chaîné. Par exemple.:
// 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",
}
// ...
});
... qui - dans A_View.js
- aura pour résultat:
events: {
"click .a_foo": "a_bar",
"click .b_foo": "b_bar",
"click .c_foo": "c_bar",
}
Si vous êtes certain que la ParentView
a les événements définis en tant qu'objet et que vous n'avez pas besoin de définir des événements de manière dynamique dans ChildView
, il est possible de simplifier davantage la réponse de soldier.moth en supprimant la fonction et en utilisant directement _.extend
:
var ParentView = Backbone.View.extend({
events: {
'click': 'onclick'
}
});
var ChildView = ParentView.extend({
events: _.extend({}, ParentView.prototype.events, {
'click' : 'onclickChild'
})
});
Wow, beaucoup de réponses ici, mais je pensais en offrir une de plus. Si vous utilisez la bibliothèque BackSupport, elle propose extend2
. Si vous utilisez extend2
, il se charge automatiquement de fusionner events
(ainsi que defaults
et des propriétés similaires) pour vous.
Voici un exemple rapide:
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
Pour le faire entièrement dans la classe parent et prendre en charge un hachage d'événements basé sur la fonction dans la classe enfant afin que les enfants puissent être agnostiques en héritage (l'enfant devra appeler MyView.prototype.initialize
s'il remplace 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);
};
}
});
Cette solution CoffeeScript a fonctionné pour moi (et prend en compte la suggestion de @ soldier.moth):
class ParentView extends Backbone.View
events: ->
'foo' : 'doSomething'
class ChildView extends ParentView
events: ->
_.extend({}, _.result(ParentView.prototype, 'events') || {},
'bar' : 'doOtherThing')
Un motif pour cela que j'aime beaucoup est la modification du constructeur et l'ajout de fonctionnalités supplémentaires:
// 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'
});
}
});
Je préfère cette méthode car vous n'avez pas à identifier le parent - une variable de moins à modifier. J'utilise la même logique pour attributes
et defaults
.