Магистральный вид: наследование и продление событий от родительского

Документация на базовую станцию ​​гласит:

Свойство events также может быть определено как функция, которая возвращает хеш событий, чтобы упростить программное определение ваших событий, а также наследовать их из родительских представлений.

Как вы наследуете события родительского представления и расширяете их?

Родительский просмотр

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

Детский просмотр

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

Ответ 1

Один из способов:

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

Ответ 2

Ответ солдата. хороший. Упрощая это, вы можете просто сделать следующее

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

Затем просто определите свои события в любом классе обычным способом.

Ответ 3

Вы также можете использовать метод defaults, чтобы избежать создания пустого объекта {}.

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

Ответ 4

Если вы используете CoffeeScript и задаете функцию events, вы можете использовать super.

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

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

Ответ 5

Не было бы проще создать специализированный базовый конструктор из Backbone.View, который обрабатывает наследование событий по иерархии.

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, переопределяющее функцию расширения, мы можем иметь подвид (например, AppView, SectionView), которые хотят наследовать свои объявленные родительским представлением события, просто сделав это, перейдя из BaseView или одного из его производных.

Мы избегаем необходимости программно определять наши функции событий в наших подзонах, которые в большинстве случаев должны явно ссылаться на родительский конструктор.

Ответ 6

Краткая версия @soldier.moth Последнее предложение:

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

Ответ 7

Это также сработает:

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

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

Использование прямой super для меня не работало, либо вручную указывал ParentView, либо унаследованный класс.

Доступ к _super var, который доступен в любом файле coffeescript Class … extends …

Ответ 8

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

Ответ 9

Для базовой версии 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",
}

Ответ 10

Я нашел более интересные решения в этой статье

Это использование Backbones super и ECMAScripts 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 и атрибутов.

Этот пример не заботится о свойствах, заданных функцией, но автор статьи предлагает решение в этом случае.

Ответ 11

Это решение CoffeeScript работало для меня (и учитывает предложение @soldier.moth):

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

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

Ответ 12

Если вы уверены, что ParentView имеет события, определенные как объекты, и вам не нужно динамически определять события в ChildView, можно упростить ответ солдата. Откажитесь от функции и используя _.extend непосредственно:

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

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

Ответ 13

Шаблон для этого, который я люблю, модифицирует конструктор и добавляет некоторые дополнительные функции:

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

Ответ 14

Ничего себе, здесь много ответов, но я думал, что предлагаю еще один. Если вы используете библиотеку 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

https://github.com/machineghost/BackSupport

Ответ 15

Чтобы сделать это полностью в родительском классе и поддерживать хеш событий на основе функций в дочернем классе, чтобы дети могли быть агностиками наследования (ребенку придется называть 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);
    };
  }
});