Friday, February 28, 2014

Fetching Backbone Models The Safe Way

If you're using Backbone, odds are that you have several detail views for various types of objects. Before those views are rendered, you need to ensure that model has been fetched. Otherwise, you don't have anything to render, besides the template, perhaps with placeholders filling in for the real model data. Or maybe your application has already performed a fetch on the collection, in which case, we have all the data required to render the detail view of the model. But, unless you're keeping that collection synchronized, you could be rendering stale data.

One approach that has worked well for me is to implement a getModel() method in a base collection. It's essentially a replacement of the get() method. Like the get() method, getModel() will select the model from the collection, by ID, and return it. Here's what the code looks like.

getModel: function( id ) {

    var model = Backbone.Collection.prototype
        .get.call( this, id );
        
    if ( !model ) {
        var options = {};
        options[ this.model.prototype.idAttribute ] = id;
        model = new this.model( options );
        this.add( [ model ] );
    }
    
    model.fetch();

    return model;

}

You can see that the model variable is setup to hold the model itself. We do this by calling get(). Now here's the powerful part, if the model doesn't exist, we create a new one, and add it to the collection. We do so using the passed in id, and concrete collection instances are able to use this method because we're relying on the idAttribute of the model prototype.

That being said, we can now safely call fetch() on the model, then return it. This is useful because it's one method, shared by all the collections in the application. It's safe because views that use getModel() will always get a model instance in return. Even if the method has to create a new model instance on the fly, meaning there's no real data for the view to render, we can still setup event listeners on the returned model. When the fetch() call does return with data, our views are already listening for it.