Представление магистрали: наследовать и расширять события от родителя
документация магистрали гласит:
свойство events также может быть определено как функция, которая возвращает хэш событий, чтобы упростить программное определение ваших событий, а также наследовать их от родительских представлений.
Как вы наследуете события родительского представления и расширяете их?
Родительский Вид
var ParentView = Backbone.View.extend({
events: {
'click': 'onclick'
}
});
Посмотреть Ребенка
var ChildView = ParentView.extend({
events: function(){
????
}
});
15 ответов:
один из способов-это:
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' } });чтобы проверить, является ли событие функцией или объектом
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); } });
Если вы используете CoffeeScript и установите функцию в
events, вы можете использоватьsuper.class ParentView extends Backbone.View events: -> 'foo' : 'doSomething' class ChildView extends ParentView events: -> _.extend {}, super, 'bar' : 'doOtherThing'
не было бы проще создать специализированный базовый конструктор из 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 }Это позволяет нам уменьшить(объединить) хэш событий вниз по иерархии всякий раз, когда мы создаем новый 'подкласс'(дочерний конструктор) с помощью переопределенной функции расширения.
# 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: .... }создавая специализированный вид: BaseView, который переопределяет функцию extend, мы можем иметь подвиды (например, AppView, SectionView), которые хотят чтобы наследовать объявленные события родительского представления, просто сделайте это, расширяясь от BaseView или одного из его производных.
мы избегаем необходимости программно определять наши функции событий в наших подвидах, которые в большинстве случаев должны явно ссылаться на родительский конструктор.
короткая версия @soldier.последнее предложение мотылька:
var ChildView = ParentView.extend({ events: function(){ return _.extend({}, _.result(ParentView.prototype, 'events') || {}, { 'click' : 'onclickChild' }); } });
Это также будет работать:
class ParentView extends Backbone.View events: -> 'foo' : 'doSomething' class ChildView extends ParentView events: -> _.extend({}, _.result(_super::, 'events') || {}, 'bar' : 'doOtherThing')используя прямые
superне работает для меня, либо вручную указатьParentViewили унаследованного класса.к
_supervar, который доступен в любом coffeescriptClass … 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 */ } });
для магистральной версии 1.2.3,
__super__работает нормально, и может даже быть закован. Например:// 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); }, ... });вы также можете сделать это для ui и атрибуты.
этот пример не заботится о свойствах, заданных функцией, но автор статьи предлагает решение в таком случае.
Это решение CoffeeScript работало для меня (и учитывает @soldier.предложение мотылька):
class ParentView extends Backbone.View events: -> 'foo' : 'doSomething' class ChildView extends ParentView events: -> _.extend({}, _.result(ParentView.prototype, 'events') || {}, 'bar' : 'doOtherThing')
Если вы уверены, что
ParentViewимеет события, определенные как объект, и вам не нужно динамически определять события в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' }); } });Я предпочитаю этот метод, потому что вам не нужно идентифицировать родительскую переменную, чтобы изменить ее. Я использую ту же логику для
attributesиdefaults.
Вау, здесь много ответов, но я думал, что предложу еще один. Если вы используете библиотеку BackSupport, она предлагает
extend2. Если вы используетеextend2он автоматически заботится о слиянииevents(а такжеdefaultsи аналогичные свойства) для вас.вот небольшой пример:
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
чтобы сделать это полностью в родительском классе и поддерживать хэш событий на основе функции в дочернем классе, чтобы дети могли быть агностиками наследования (ребенок должен будет вызвать
MyView.prototype.initializeесли он переопределяет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); }; } });
Comments