wake-up-neo.com

Backbone-Ansicht: Ereignisse vom übergeordneten Element übernehmen und erweitern

Backbones Dokumentation besagt:

Die events -Eigenschaft kann auch als Funktion definiert werden, die einen Ereignis-Hash zurückgibt, um die programmgesteuerte Definition Ihrer Ereignisse zu erleichtern und sie von übergeordneten Ansichten zu erben.

Wie erben Sie die View-Events eines Elternteils und erweitern Sie diese?

Übergeordnete Ansicht

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

Kinderansicht

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

Ein Weg ist:

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

Ein anderes wäre:

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

Um zu prüfen, ob Ereignisse eine Funktion oder ein Objekt sind

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

Die Antwort der Soldaten ist gut. Wenn Sie es weiter vereinfachen, können Sie Folgendes tun

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

Dann definieren Sie einfach Ihre Ereignisse in einer der beiden Klassen auf typische Weise.

79
34m0

Sie können auch die Methode defaults verwenden, um zu vermeiden, dass das leere Objekt {} erstellt wird.

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

Wenn Sie CoffeeScript verwenden und eine Funktion auf events setzen, können Sie super verwenden.

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

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

Wäre es nicht einfacher, aus Backbone.View einen spezialisierten Basiskonstruktor zu erstellen, der die Vererbung von Ereignissen in der Hierarchie übernimmt.

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
}

Dies ermöglicht es uns, den Ereignishash in der Hierarchie nach unten zu reduzieren (zusammenzuführen), wenn wir eine neue Unterklasse (untergeordneter Konstruktor) erstellen, indem Sie die neu definierte Erweiterungsfunktion verwenden.

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

Durch das Erstellen einer spezialisierten Ansicht: BaseView, die die Erweiterungsfunktion neu definiert, können Unteransichten (wie AppView, SectionView) erstellt werden, die die deklarierten Ereignisse ihrer übergeordneten Ansicht erben möchten, indem sie einfach von BaseView oder einer ihrer Ableitungen erweitert werden. 

Wir vermeiden die programmatische Definition unserer Ereignisfunktionen in unseren Unteransichten, die in den meisten Fällen explizit auf den übergeordneten Konstruktor verweisen müssen.

6
Shaw W

Das würde auch funktionieren:

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

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

Die Verwendung von straight super hat für mich nicht funktioniert, entweder wurde manuell die ParentView oder die geerbte Klasse angegeben. 

Zugriff auf die _super var, die in jedem Kaffeeskript verfügbar ist Class … extends …

2
yolk

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

2
vovan

Kurze Version von @ soldier.moths letztem Vorschlag:

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

Ich habe in diesem Artikel eine interessantere Lösung gefunden.

Die Verwendung von super des Backbones und hasOwnProperty von ECMAScript. Das zweite seiner progressiven Beispiele wirkt wie ein Zauber. Hier ist ein bisschen ein 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);
    },
    ...
});

Sie können dies auch für ui und attributes tun.

In diesem Beispiel werden die von einer Funktion festgelegten Eigenschaften nicht berücksichtigt. In diesem Fall bietet der Autor des Artikels jedoch eine Lösung.

1
firebird631

Für Backbone-Version 1.2.3 funktioniert __super__ gut und kann sogar verkettet werden. Z.B.:

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

... was in A_View.js zu Folgendem führt:

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

Wenn Sie sicher sind, dass für ParentView die Ereignisse als Objekt definiert sind und Sie die Ereignisse nicht dynamisch in ChildView definieren müssen, können Sie die Antwort von soldier.moth weiter vereinfachen, indem Sie die Funktion loslassen und _.extend direkt verwenden:

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

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

Wow, viele Antworten hier, aber ich dachte, ich würde noch mehr anbieten. Wenn Sie die BackSupport-Bibliothek verwenden, wird extend2 angeboten. Wenn Sie extend2 verwenden, sorgt es automatisch für das Zusammenführen von events (sowie defaults und ähnlichen Eigenschaften).

Hier ein kurzes Beispiel:

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

0
machineghost

Um dies vollständig in der übergeordneten Klasse zu tun und einen funktionsbasierten Ereignishash in der untergeordneten Klasse zu unterstützen, können Kinder die Vererbung agnostisch machen (das Kind muss MyView.prototype.initialize aufrufen, wenn es initialize überschreibt):

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);
    };
  }
});
0
Kevin Borders

Diese CoffeeScript-Lösung hat für mich funktioniert (und berücksichtigt @ soldier.moths Vorschlag):

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

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

Ein Muster, das ich so sehr mag, ist das Ändern des Konstruktors und das Hinzufügen zusätzlicher Funktionen:

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

});

Ich bevorzuge diese Methode, weil Sie nicht die übergeordnete Variable identifizieren müssen, um sie zu ändern. Ich verwende dieselbe Logik für attributes und defaults.

0
jtrumbull