Backbone’s flexible design makes it really easy to structure apps any way you see fit. But it also means that you’ll always need to augment Backbone if you have a decent-sized project—which is fine because you can tweak almost everything very easily. But the one piece of Backbone you can’t easily augment is the configuration logic for a class, and that’s frustrating!

What’s the problem?

First a little intro… When a backbone instance is created, the class’s constructor gets called. constructor sets up all of the default configuration that the Backbone instance needs (eg. making a cid, parsing this.attributes on a Model, copying special options on a View). The problem is that the Backbone constructor does these things inline and thenimmediately calls initialize. There’s not spot in there before initialize is called to augment the default configuration.

constructor : function (attributes, options) {
    // Adding configuration here means you don't have access to and
    // can't override any configuration that Backbone core does.
    Backbone.Model.prototype.constructor.apply(this, arguments);
    // Adding configuration here is impossible because `initialize` 
    // has already been called.
}

This is a legitimate need:

  • Say I want to change the way all my Models work by copying the options that get passed in onto this.options, to match how Views treat options.

  • Or maybe I want to add a way for Models to store extra UI state in a this.state property instead of inside this.attributes.

  • Or maybe I want to have my Models inherit defaults all the way up the inheritance chain by _.defaulting them with their parent’s defaults.

Some of you might be thinking “just call it in initialize!”, which, yes, does technically work. But then I’d have to call it in every initialize throughout my entire app, which is really annoying when I know up front I want every single Model I make to be have this customization.

See, it’s common practice to make “base” classes in Backbone to augment Backbone core with custom logic. Instead of extending from Backbone.Model for all of your models, you always extend from BaseModel.

That approach works well for customization on a per-class level. By that I mean: if you have an AuthorModel, a BookModel, and a PageModel, they’ll all need different initialization logic, which will go in each class’s custom initialize function. Easy!

It also works well for customization on a class-wide level for anything after the initial configuration. By that I mean: despite AuthorModel and BookModel being very different, they might both make use of a custom toJSON method you’ve defined in BaseModel. Also easy.

Where the whole thing breaks down is when you want to customize the configuration on a class-wide level. There’s nothing to hook onto to augment the configuration logic of anything that extends from Backbone.Model before initialize is called because all of the configuration logic is inlined in the constructor.

The three places to augment Backbone.

There are three places you might want to add custom logic to a Backbone class. The first two are nicely handled, but there isn’t a nice way to handle the third currently:

  • Before an instance is configured. Here you could provide your own augmented constructor and call Backbone’s constructor afterwards.

  • After it’s initialized. This is completely up to you, and most of what you’re writing as a Backbone user fits in this category.

  • After it’s configured, but before it’s initialized. This is where you’d add your own custom configuration logic, but Backbone doesn’t give you a nice way to do this! There are only two different annoying ways and one incomplete way I could think to do it:

Augment the constructor. (Incomplete!)

constructor : function (attributes, options) {
    this._configureOptions();
    Backbone.Model.prototype.constructor.apply(this, arguments);
}

That does work, but only if your added logic doesn’t rely on anything else Backbone does in its constructor. If, instead, you need to access this.attributes in a Model, which have yet to be parsed, you’re out of luck. Or if need to reference the cid? tough. Or if you want to undo part of Backbone’s default configuration? no dice.

Re-call your logic in every single initialize method. (Annoying!)

var AuthorModel = BaseModel.extend({
    initialize : function (attributes, options) {
        this._configureOptions();
        // Real initialization logic.
    }
});

var BookModel = BaseModel.extend({
    initialize : function (attributes, options) {
        this._configureOptions();
        // Real initialization logic.
    }
});

var PageModel = BaseModel.extend({
    initialize : function (attributes, options) {
        this._configureOptions();
        // Real initialization logic.
    }
});

Yeah I could do that. But I shouldn’t need to keep copying and pasting that code around all over my codebase when I know upfront that I want everything that extends BaseModel to be configured that way.

Give up initialize and create a new initialize. (Annoying!)

initialize : function (attributes, options) {
    this._configureOptions();
    this.start.apply(this, arguments);
}

start : function (attributes, options) {
    // Real initialization logic.
}

That’s also not great. Everyone who reads my code needs to be informed that what they always refer to as initialize is now start. And any time I’m talking/reading about Backbone I have to mentally convert initialize to start and back. And if you accidentally override initialize instead, you might not even realize it until things get weird because the instance wasn’t configured properly.

So what’s a solution?

My suggestion is to give all the Backbone classes a configure method that does the configuration that happens in the constructor. Sound familiar? That’s because Backbone.View already has a _configure method that makes this kind of augmentation incredibly easy. But they all should! Here’s what adding class-wide configuration logic would look like:

configure : function (attributes, options) {
    Backbone.Model.prototype.configure.apply(this, arguments);
    // Add custom logic here.
}

You just augment configure in your BaseModel and then you don’t ever need to touch it again! All of your other extended Models will get the augmented configuration options. Class-wide configuration solved!

If you agree, or have a better way of acheiving the same goal, I’d love it if you’d contribute to the issue I opened on Github.