Thursday, March 27, 2014

Storing Backbone Views In Element Data

The data() jQuery function let's us store arbitrary data in an element. It doesn't actually store it in the DOM, but it's an elegant means to associate arbitrary application data with an element. It removes unnecessary code and complexity — let jQuery handle it. I often find myself having to create structures in my code that look similar to what I have in the DOM, essentially duplicating it. There's often no need for this, because if it's data that's specifically related to an element, that's what data() is for.

Backbone views are one such example of something that could be stored this way. Views have an element, all the time. So rather than try to keep track of the view references inside your JavaScript code, just have the view attach itself of the element it's managing. Here's an example. In particular, here's the router code that instantiates the views.

var app = new (Backbone.Router.extend({
    
    routes: {
        'user/:id': 'routeUser',
        '*default': 'routeDefault'
    },
    
    routeUser: function( id ) {
        this.cleanup();
        new UserView({
            model: users.get( id )
        }).render();
    },
    
    routeDefault: function() {
        this.cleanup();
        new MainView().render();
    },
    
    cleanup: function() {
        var view = $( 'body' ).data( 'view' );
        if ( view ) {
            view.remove();    
        }
    }
    
}))();

In both of these routes, we're just creating the instances, without actually storing the references, that's handled by the view itself. But before we do that, we use the cleanup() method, which actually deletes the existing view, if there is one. It does so by looking for view data in the body element. But how does the view instance get there in the first place?

var UserView = Backbone.View.extend({
    initialize: function() {
        this.$el.appendTo(
            $( 'body' ).data( 'view', this ) );
    }
});

Here, the view takes care of storing itself in the body element when it's first instantiated. This example does assume a fairly simple application structure, as in, only one view at a time. But the same approach could be used in a more elaborate scenario. For example, storing the view in the element itself, which makes the cleanup effort a little more difficult, but not impossible. For something like that, the :data selector would come in handy.