web-dev-qa-db-fra.com

Vue du réseau principal: hériter et étendre les événements du parent

Les états de documentation de backbone:

La propriété des événements peut également être défini comme une fonction qui renvoie un hachage d'événements, pour le rendre plus facile de définir vos événements par programmation, ainsi que les vues hériter de parents.

Comment pouvez-vous héritez un des événements de vue des parents et de les étendre?

Voir parent

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

Enfants

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

Une façon est:

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

Un autre serait:

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

Pour vérifier si Events est une fonction ou un objet

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

La réponse du soldat est bonne. En simplifiant davantage, vous pouvez simplement faire ce qui suit

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

Ensuite, définissez simplement vos événements dans l'une ou l'autre classe.

79
34m0

Vous pouvez également utiliser la méthode defaults pour éviter de créer la {} objet vide.

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

Si vous utilisez CoffeeScript et définissez une fonction sur events, vous pouvez utiliser super.

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

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

Ne serait-il pas plus facile de créer un constructeur de base spécialisé à partir de Backbone.View qui gère l'héritage des événements dans la hiérarchie.

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
}

Cela nous permet de réduire (fusionner) le hachage des événements dans la hiérarchie chaque fois que nous créons une nouvelle «sous-classe» (constructeur enfant) à l'aide de la fonction d'extension redéfinie.

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

En créant une vue spécialisée: BaseView qui redéfinit la fonction d’extension, nous pouvons avoir des sous-vues (telles que AppView, SectionView) qui souhaitent hériter des événements déclarés de leur vue parent simplement en procédant à une extension depuis BaseView ou l’un de ses dérivés. 

Nous évitons la nécessité de définir par programme nos fonctions d'événement dans nos sous-vues, qui dans la plupart des cas doivent faire explicitement référence au constructeur parent.

6
Shaw W

Cela fonctionnerait aussi:

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

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

Utiliser straight super ne fonctionnait pas pour moi, soit spécifiait manuellement la classe ParentView ou héritée. 

Accès à la _super var disponible dans n'importe quel coffeescript 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

Version courte de la dernière suggestion de @ soldier.my:

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

J'ai trouvé des solutions plus intéressantes dans cet article article

Il utilise le super de Backbone et hasOwnProperty de ECMAScript. Le second de ses exemples progressifs fonctionne à merveille. Voici un peu un 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);
    },
    ...
});

Vous pouvez également le faire pour ui et attributs.

Cet exemple ne prend pas en charge les propriétés définies par une fonction, mais l'auteur de l'article propose une solution dans ce cas.

1
firebird631

Pour la version 1.2.3 de Backbone, __super__ fonctionne correctement et peut même être chaîné. Par exemple.:

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

... qui - dans A_View.js - aura pour résultat:

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

Si vous êtes certain que la ParentView a les événements définis en tant qu'objet et que vous n'avez pas besoin de définir des événements de manière dynamique dans ChildView, il est possible de simplifier davantage la réponse de soldier.moth en supprimant la fonction et en utilisant directement _.extend:

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

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

Wow, beaucoup de réponses ici, mais je pensais en offrir une de plus. Si vous utilisez la bibliothèque BackSupport, elle propose extend2. Si vous utilisez extend2, il se charge automatiquement de fusionner events (ainsi que defaults et des propriétés similaires) pour vous.

Voici un exemple rapide:

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

Pour le faire entièrement dans la classe parent et prendre en charge un hachage d'événements basé sur la fonction dans la classe enfant afin que les enfants puissent être agnostiques en héritage (l'enfant devra appeler MyView.prototype.initialize s'il remplace 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);
    };
  }
});
0
Kevin Borders

Cette solution CoffeeScript a fonctionné pour moi (et prend en compte la suggestion de @ soldier.moth):

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

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

Un motif pour cela que j'aime beaucoup est la modification du constructeur et l'ajout de fonctionnalités supplémentaires:

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

});

Je préfère cette méthode car vous n'avez pas à identifier le parent - une variable de moins à modifier. J'utilise la même logique pour attributes et defaults.

0
jtrumbull