There is a better way to write Backbone applications and here I hope to write a quick primer. The topics I will cover in this series will follow these basics:

  1. Everything-from-one-container
  2. Pass-through views
  3. Declarative rendering
  4. Abstract away element access (TBD)
  5. ViewModels over Models (TBD)
  6. BONUS ROUND Coffeescript (TBD)

Declarative rendering

Rendering is one of the more complicated aspects of creating single page javascript applications. It has implications in performance, responsiveness, debug-ability, testability and can have a steep learning curve. For the most part, rendering falls into one of two categories: implicit and declarative.

Implicit rendering can be put another way: render only when I tell you to render. So, our rendering code would look something like this this.childView.render() and it might be stashed in a block of code like this:

newMessage: function(from, to, message) {
  var message = this.messages.create({
        from: from, to: to, message: message
    });
  this.messageView.render(message);
}

The characteristics of this method are:

  1. Render is called explicitly by a line of code that the developer is writing.
  2. Data is often passed in as an argument to the render method.
  3. Views are passed around to services, controllers and (maybe) collections in order to have access to direct rendering.

This method is a very straight-forward, easy to read method of rendering views. The other method, declarative rendering, would look more like this:

Service Code

newMessage: function(from, to, message) {
    this.messages.create({
        from: from, to: to, message: message
    });
}

View Code

initialize: function(config) {
    this.messages = config.messages;
    this.messages.on('add', $.proxy(this.render, this));
},

render: function() {
    _.template('some-template', { messages: this.messages });
}

In this snippet, we see that the view and the service/controller are completely separated. The view gets triggered through an event that is fired by the Backbone Collection (built in behavior). The code snippet itself is a bit larger, but the amount of code is essentially the same. The characteristics of this approach are:

  1. Use built in (and sometimes custom) events to execute render methods.
  2. Pass in collections as part of the views constructors (so that we always have a reference).
  3. Render typically takes zero arguments and pulls data from the encapsulated state.

This code is more disjoint than the explicit code above. That is, it is more decoupled. I argue that this is a good thing because:

  1. Entanglement. When we have services that are explicitly calling render on views, they become entangled with the action of rendering those views. For example, imagine that the clearSelection and selectItem from Part 2 did explicit rendering calls. When we called selectItem, there would be an extra render call that we probably did not want to do. In order to separate that out, we would need to create another method (perhaps clearSelectionAndRender). This very quickly explodes the number of methods in our classes and makes following the logic difficult.
  2. Performance. Because of entanglement, we will often miss that we are rendering a view multiple times before we finish responding to a single user action. Declarative rendering reduces (though does not eliminate) much of this mistaken rendering.
  3. Testing. It is much easier to test both the view and the service/controllers in isolation when they are dependent on less objects.
  4. Declarative view rendering, in a rather unintuitive sense, makes readability of our views better. For example, if we read the second example's view code above in isolation, we grasp that the view is only going to render on "add" of the messages element. This may be what we want or we may want to change the binding to "add remove", "change" or "fetch add".

Conclusion

With declarative view rendering, we have decoupled parts of our Backbone app and have really started to separate concerns of our Model layer (represented by Collections and Models), the View layer and the Controller/Service layer. We start to see that the services are not driving views but are interacting with models. Views are merely responding to and displaying changes in the models. And views are delegating user actions to the controllers.

In the next part of this series, we are going to finalize our views with a little element access massaging. Stay tuned!