tutorial ejemplos collection backbone javascript backbone.js backbone-events

javascript - ejemplos - Vista Backbone: hereda y extiende eventos desde el padre



backbone view events (15)

La documentación de Backbone dice:

La propiedad de eventos también se puede definir como una función que devuelve un hash de eventos, para facilitar la definición programática de sus eventos, así como para heredarlos de las vistas principales.

¿Cómo heredas los eventos de vista de un padre y los extiendes?

Vista de padres

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

Vista de niño

var ChildView = ParentView.extend({ events: function(){ ???? } });


¿No sería más fácil crear un constructor base especializado desde Backbone.View que maneje la herencia de eventos en la jerarquía?

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 }

Esto nos permite reducir (fusionar) el hash de eventos en la jerarquía cada vez que creamos una nueva ''subclase'' (constructor hijo) utilizando la función de extensión redefinida.

# 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: .... }

Al crear una vista especializada: BaseView que redefine la función extender, podemos hacer que las subvistas (como AppView, SectionView) que desean heredar los eventos declarados de su vista principal simplemente lo hagan al extender desde BaseView o uno de sus derivados.

Evitamos la necesidad de definir programáticamente nuestras funciones de eventos en nuestras subvistas, que en la mayoría de los casos deben referirse explícitamente al constructor padre.


Encontré soluciones más interesantes en este article

Se usa el súper del Backbone y el hasOwnProperty de ECMAScript. El segundo de sus ejemplos progresivos funciona como un encanto. Aquí hay un poco de código:

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); }, ... });

También puedes hacer eso para ui y atributos .

Este ejemplo no se ocupa de las propiedades establecidas por una función, pero el autor del artículo ofrece una solución en ese caso.


Esta solución CoffeeScript funcionó para mí (y tiene en cuenta la sugerencia de @ soldier.moth):

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


Esto también funcionaría:

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

Usar super recta no funcionaba para mí, ya sea manualmente especificando la clase heredada o ParentView .

El acceso al _super var que está disponible en cualquier Class … extends … coffeescript Class … extends …


La respuesta de soldier.moth es buena. Simplificando aún más, podrías hacer lo siguiente

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

Luego solo defina sus eventos en cualquier clase de la manera típica.


Para Backbone versión 1.2.3, __super__ funciona bien, e incluso puede estar encadenado. P.ej:

// 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", } // ... });

... que - en A_View.js - dará como resultado:

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


Para hacer esto completamente en la clase principal y admitir un hash de eventos basado en función en la clase secundaria para que los niños puedan ser independientes de la herencia (el niño tendrá que llamar a MyView.prototype.initialize si anula la 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); }; } });


Si está seguro de que ParentView tiene los eventos definidos como objeto y no necesita definir eventos dinámicamente en ChildView , es posible simplificar la respuesta de soldier.moth al deshacerse de la función y usar _.extend directamente:

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


Si usa CoffeeScript y establece una función para los events , puede usar super .

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


También puede usar el método defaults para evitar crear el objeto vacío {} .

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


Un patrón para esto que me gusta es modificar el constructor y agregar alguna funcionalidad adicional:

// 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'' }); } });

Prefiero este método porque no tiene que identificar el padre: una variable menos para cambiar. Uso la misma lógica para los attributes y los defaults .


Una forma es:

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

Otro sería:

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'' } });

Para verificar si Eventos es función u objeto

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


Versión corta de la última sugerencia de @ soldier.moth:

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


Wow, muchas respuestas aquí, pero pensé que ofrecería una más. Si usa la biblioteca BackSupport, ofrece extend2 . Si utiliza extend2 , automáticamente se encarga de fusionar los events (así como los defaults y las propiedades similares) por usted.

Aquí hay un ejemplo rápido:

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


// 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/