Extending Backbone.js
A lot of developers love Backbone.js because of the great functionality that it gives you out of the box. It gives you a solid starting point to implement well-organized MVC/MV* client-side applications in JavaScript. However, there will eventually come a time where the default classes — the Model, View, Collection classes as well as the Events and Router classes (and remember in JavaScript the term “classes” is used very loosely) might not do everything that you want your application to do in all places. In what follows, we’ll look at a couple of different methods of extending some of the components of Backbone.js in potentially useful ways.
So let’s get to it…
Extending Classes
Extending the default “classes” (cynical air quotes) is surprisingly a lot easier that one might first think. All you have to do to extend the Backbone’s Model, View, Collection etc. classes is add to the prototype after Backbone has been loaded. For example, something that I have found to be useful is to have a quick ability for a view to trigger a route to redirect somewhere else after a successful (or failing) action. There are some Backbone developers who have argued that you shouldn’t trigger routes from views, but I personally don’t see a huge issue with it and if you’re careful you can avoid things being too tightly coupled. Right after I instantiate my new router like so…
var Router = new Router();
And since that’s usually the only router that I’ll be using, I can do something like the following…
Backbone.View.prototype.goTo = function (route) {
Backbone.history.navigate(route, true);
};
Backbone.history.navigate is the method that is inside the router’s navigate that gets called when a route event is triggered. The second argument is set to ‘true’ because it corresponds to the trigger option which tells Backbone to actually trigger the route and not just update the URL string. Rather than just write this out every time, in our views we can now just call the following from our view inside of some event-handler or somewhere else…
this.goTo('home');
What this allows us to do is trigger the router navigate method from anywhere within our view. This makes it easy to redirect wherever we want. Pretty neat, eh? Adding something like this to the Backbone View prototype is not absolutely necessary, but the point of this exercise is to show just how easy it is to extend the Backbone base classes. You could just as easily add new methods to all Models or all Collections if you wanted to. Just make sure you don’t overwrite the existing ones! If you write out something like Backbone.View.prototype.initialize = function() { }, you’re gonna have a bad time.
Creating New Classes
But maybe we have some cases where we have some aspect of functionality that we want to use in our Backbone application, but they don’t really fall under the category of Model, View, Collection, etc. Well in this case, we can actually create a new class by borrowing some core code found inside of the Backbone.js file. We can essentially use the same implementation of inheritance that Backbone.js uses to create a new class that can be used in the same manner that Models, Views, Collections, and others are. If you put something like the following into a file (let’s just call it backbone.helper.js for example) and then load it up after you load Backbone, you can have a new Backbone.js class!
(function() {
var Backbone = this.Backbone;
// Events object that we're going to extend in the same way other Backbone classes are...
var Events = Backbone.Events;
var Helper = Backbone.Helper = function(attributes, options) {
this.initialize.apply(this, arguments);
};
// add base methods here
_.extend(Helper.prototype, Events, {
initialize: function() { },
});
/*** The following is pulled straight from Backbone.js ***/
// Helper function to correctly set up the prototype chain, for subclasses.
// Similar to `goog.inherits`, but uses a hash of prototype properties and
// class properties to be extended.
var extend = function(protoProps, staticProps) {
var parent = this;
var child;
// The constructor function for the new subclass is either defined by you
// (the "constructor" property in your `extend` definition), or defaulted
// by us to simply call the parent's constructor.
if (protoProps && _.has(protoProps, 'constructor')) {
child = protoProps.constructor;
} else {
child = function(){ return parent.apply(this, arguments); };
}
// Add static properties to the constructor function, if supplied.
_.extend(child, parent, staticProps);
// Set the prototype chain to inherit from `parent`, without calling
// `parent`'s constructor function.
var Surrogate = function(){ this.constructor = child; };
Surrogate.prototype = parent.prototype;
child.prototype = new Surrogate;
// Add prototype properties (instance properties) to the subclass,
// if supplied.
if (protoProps) _.extend(child.prototype, protoProps);
// Set a convenience property in case the parent's prototype is needed
// later.
child.__super__ = parent.prototype;
return child;
};
/*** END Pulled straight from Backbone.js ***/
Helper.extend = extend;
}).call(this);
A lot of the code above was pulled directly from Backbone. It uses the same methods to add to the Backbone “namespace” and sets up the same inheritance pattern that Backbone.js uses. Now you could declare a new helper in the following manner (just as you do for Backbone’s models, collections and views)…
var Helper = Backbone.Helper.extend({
sayHi: function() {
console.log('Hello! I am a helpful helper...')
}
});
And then instantiate it and call the methods you define…
var helpfulHelper = new Helper(); helpfulHelper.sayHi();
Note that if you are using this with RequireJS in your Backbone application and what to use something like this, it’s a good idea to create a shim for it and declare Backbone as a dependency. More information on RequireJS can be found here, and also on the official RequireJS website. I have run into issues where Backbone was undefined unless this was specified…
require.config({
paths: {
'jquery': 'libs/jquery/jquery-1.9.1',
'underscore': 'libs/underscore/underscore',
'backbone': 'libs/backbone/backbone',
'helper': 'libs/helper/helper',
},
shim: {
'underscore': {
exports: '_',
},
'backbone': {
deps: ['underscore', 'jquery'],
exports: 'Backbone',
},
'helper': {
deps:['backbone']
}
}
});
So there you have it. We’ve looked at a couple of different ways to extend Backbone where you can add functionality to your Backbone.js application. So take that ball and run with it and happy extending!
Backbone.js, JavaScript, RequireJS





0 Responses to Extending Backbone.js