Proper way of doing view mixins in Backbone

24,510

Solution 1

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"

Solution 2

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.

Solution 3

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

Solution 4

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

Solution 5

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
Share:
24,510
Mauvis Ledford
Author by

Mauvis Ledford

Mauvis Ledford is a hands-on, full-stack CTO specializing in modern, responsive web applications and scaling on virtualized cloud environments. His love is to build it, make it scale, and make it beautiful. As a life long learner, Mauvis has spent 10 years honing his skills in the tech industry from design and development, to web performance and security, to consulting and team management. He loves to learn and teach what he’s learned to others. Beyond mastering the technical aspects of his job, Mauvis’s interests lie in rapid prototyping, user testing, hiring star engineers, and improving products through analytics. He supports both Scrum and Lean Startup methodologies. For his technical blog on JavaScript, front-end mobile development, emacs, and other meanderings check out: http://readystate4.com/ Add him up on twitters at @krunkosaurus. Checkout him out on Github at: https://github.com/krunkosaurus and https://github.com/brainswap Above all, have a great day!

Updated on July 09, 2022

Comments

  • Mauvis Ledford
    Mauvis Ledford almost 2 years

    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?