ChatGPT解决这个技术问题 Extra ChatGPT

Proper way of doing view mixins in Backbone

I extend base backbone views all the time and have a base view per section so that I can extend on multiple levels. My question is, what's the most effective way of doing view mixins: reusable view partials that can be mixed in to any view. For example:

var BaseProfile = Backbone.View.extend({ ...});
var UserProfile = BaseProfile.extend({ ...});
var VideoSupport = Backbone.View.extend({ ...});

What's the best way to mixin VideoSupport view (an event object and a few methods) with UserProfile view?


T
TheCloudlessSky

The underscore.js library provides an extend method that does what you want. You can define functionality on any object, and then quite literally copy & paste all of the methods and attributes from that object to another.

Backbone's extend methods on Views, Models, and Routers are a wrapper around underscore's extend.

 var MyMixin = {
  foo: "bar",
  sayFoo: function(){alert(this.foo);}
}

var MyView = Backbone.View.extend({
 // ...
});

_.extend(MyView.prototype, MyMixin);

myView = new MyView();
myView.sayFoo(); //=> "bar"

Thanks Derek, I suppose _.defaults would be more appropriate so that mixin views properties and methods don't override. What would be proper though, is if the mixin would extend the events and init function of the "class" it's mixing in to. Also in your example would you not extend MyView.prototype instead of the MyView function class?
This is the way to go, but yes with the caveat that you usually want to extend the prototype.
@maxl0rd - Exactly. I've changed it to reflect the most common case. Derick, of all people, knows this, but I thought I'd reflect the answer to the most common case.
oops. :) yeah - that should have been on the prototype. thanks for the edit
+1 to the comment about using _.defaults instead of _.extend .. With the caveat that people might not understand what Derick was doing since defaults is a much less widely known function.
D
Dan S

I might recommend using Backbone.Cocktail which provides a really succinct way of specifying mixins (that respect inheritance):

var Mixin = {
  initialize: function() {
    console.log("I'll be called as well as the class's constructor!");
  }
};

var View = Backbone.View.extend({
  mixins: [ MyMixin ]
});

I've detailed it in this blog post.


+1 for Cocktail. This solution, unlike the accepted answer, merges the mixin and View events hashes (and any other objects, like Marionette's ui or modelEvents hashes).
u
user873792

you can use this gist https://gist.github.com/3652964


While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes.
@S.L.Barth - While I agree that it's more helpful to have the code inline - this is a system with community editing abilities so feel free to copy the code here to improve the anser.
j
jifeon

You can use Backbone.Mix library which used mixins embedded to the prototype chain

var Editable = {
    edit: function(){
        console.log('edit');
    }
};

var Article = Backbone.Model.mix(Editable).extend({
    initialize: function(){
        Backbone.Model.prototype.initialize.call(this);
        this.edit(); // logs "edit"
    }
});

P
Paul Alexander

I needed the ability to override and invoke mixed in methods (ala super) closer to how ruby handles modules. And the simple extension method would clobber the mixin method if it existed in the class. Since I'm building it all in CoffeeScript I have access to the super object which lets me shim methods in. It will also automatically merge the events object so you can define event handlers in the mixin.

_.extend Backbone,
  mixin: (klass, mixin, merge) ->
    debugger unless mixin
    mixin = mixin.prototype || mixin
    merge ||= ["events"]

    sup = _.extend({},klass.__super__)

    for name,func of mixin      
      if base = sup[name] && _.isFunction(base)
        sup[name] = ->
          func.apply this, arguments
          base.apply this, arguments
      else
        sup[name] = func

    hp = {}.hasOwnProperty
    prototype = klass.prototype
    for name,func of mixin
      continue unless hp.call(mixin,name)
      continue if _(merge).contains name
      prototype[name] = func unless prototype[name]

    klass.__super__ = sup

    _(merge).each (name) ->
      if mixin[name]
        prototype[name] = _.extend({},mixin.events,prototype.events) 

    @

Usage

class SimpleView extends Backbone.View
  events:
    "click .show" : "show"

  calculate: ->
    super + 5

  show: ->
    console.log @calculate()

class CalculatableViewMixin
  events:
    "click .calculate" : "show"

  calculate: ->
    15

Backbone.mixin SimpleView, CalculatableViewMixin

I've run into some trouble using this method in IE8. Firefox, Chrome, Safari and IE9+ are working so far. Any idea why it might not work in IE8? The console in IE8 doesn't show any message at all...
i
ipbd

Another option is the Backbone.Advice which provides power of AOP-styled mixins (you can inject custom behavior before, after or around calls of extended object methods).


关注公众号,不定期副业成功案例分享
Follow WeChat

Success story sharing

Want to stay one step ahead of the latest teleworks?

Subscribe Now