主干视图:从父级继承和扩展事件

IT技术 javascript backbone.js backbone-events
2021-03-19 10:57:49

Backbone 的文档说明:

events 属性也可以定义为返回事件哈希的函数,以便更容易地以编程方式定义事件,以及从父视图继承它们。

你如何继承父级的视图事件并扩展它们?

父视图

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

子视图

var ChildView = ParentView.extend({
   events: function(){
      ????
   }
});
6个回答

一种方法是:

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'
      });
   }
});
只是在这里投入我的两分钱,我相信第二个选择是最好的解决方案。我之所以这么说是因为它是唯一真正封装的方法。唯一使用的上下文是this与必须通过实例名称调用父类。非常感谢你。
2021-04-22 10:57:49
如果我没记错的话,您应该能够使用parentEvents = _.result(ParentView.prototype, 'events');而不是“手动”检查是否events是一个函数。
2021-05-11 10:57:49
@科恩。+1 提到了_.result我以前没有注意到的下划线效用函数对于任何感兴趣的人,这里有一个 jsfiddle,在这个主题上有很多变化:jsfiddle
2021-05-11 10:57:49
@brent 当然,刚刚添加了第三个案例
2021-05-14 10:57:49
太好了...也许您可以更新它以显示您将如何从 ChildView 继承(检查原型事件是函数还是对象)...或者也许我对整个继承的东西想得太多了。
2021-05-15 10:57:49

Soldier.moth 的答案是一个很好的答案。进一步简化它,您可以执行以下操作

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

然后只需以典型方式在任一类中定义您的事件。

这个答案不再有效,因为在初始化之前调用了 delegateEvents(对于版本 1.2.3 是这样) - 在带注释的源代码中很容易做到这一点。
2021-04-29 10:57:49
@Soldier.moth,好吧,我已将其编辑为 {},ParentView.prototype.events,this.events
2021-05-05 10:57:49
显然这是有效的,但据我所知,delegateEvents在构造函数中调用它来绑定事件。所以当你将它扩展initialize到 .
2021-05-17 10:57:49
这很挑剔,但我对这个解决方案的问题是:如果您有一个多样化且丰富的视图层次结构,您将不可避免地发现自己initialize在少数情况下编写(然后也必须处理管理该功能的层次结构)只是为了合并事件对象。在我看来,保持events合并本身更干净话虽如此,我不会想到这种方法,而且被迫以不同的方式看待事物总是很好:)
2021-05-18 10:57:49
很好的调用,尽管您可能想要交换this.events&ParentView.prototype.events否则,如果两个都在同一事件上定义处理程序,则父处理程序将覆盖子处理程序。
2021-05-20 10:57:49

您还可以使用该defaults方法来避免创建空对象{}

var ChildView = ParentView.extend({
  events: function(){
    return _.defaults({
      'click' : 'onclickChild'
    }, ParentView.prototype.events);
  }
});
这会导致在子处理程序之后绑定父处理程序。在大多数情况下不是问题,但如果子事件应该取消(而不是覆盖)父事件,这是不可能的。
2021-05-13 10:57:49

如果您使用 CoffeeScript 并将函数设置为events,则可以使用super.

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

class ChildView extends ParentView
  events: ->
    _.extend {}, super,
      'bar' : 'doOtherThing'
这仅在父事件变量是函数而不是对象时才有效。
2021-04-22 10:57:49

从 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 或其派生对象之一扩展即可。

我们避免在我们的子视图中以编程方式定义我们的事件函数的需要,在大多数情况下需要显式引用父构造函数。