An Intro to Backbone.js: Part 2 – Controllers and Views
We’re good with Backbone.Model. We’re good with Backbone.Collection. We’re good with Backbone.Events. So:
Backbone.Controller:
If you’re coming from Rails land, you’ll be pretty familiar with this guy, although it’s got a pretty neat twist. You get to write your routes right in the controller. This is very, very nice to work with.
var MyController = Backbone.Controller.extend({
routes : {
"say/:something" : "say"
},
say : function(something) {
alert(something);
}
});
var yC = new MyController;
Backbone.history.start();
This is pretty straightforward. I defined a prototype and constructor for MyController, complete with one route and the property name, as a string, of the function in this Controller that you want it it to call (analogous to an action in Rails).
Backbone.history is where Backbone stores all of the places you’ve been. This is necessary in order to not break the back button in your browser.
When I called the constructor, MyController gave all of its’ routes to Backbone.history, along with their relevant callbacks.
Next, when I called Backbone.history.start(), Backbone subscribed to the browser’s hashchange event*. Whenever the hash changes, Backbone.history takes care of running your Controller’s function.
*If the browser supports it. Otherwise, it starts monitoring the window.location for hashchanges periodically.
Now if I navigate to “http://myapp.root/#say/hello_world” … I get a butt ugly alert with “hello_world” in it.
Backbone.Views:
Views are designed to encapsulate a DOM element’s functionality. It exists between a Model/Collection and the DOM.
The View has the following responsibilities:
- rendering the DOM element
- initializing DOM events for user interactions within the element
- subscribing to events that are relevant to the DOM element and updating it appropriately, such as:
- When a Model changes.
- When a Collection changes.
- When a specific event occurs elsewhere on the page (navigation, etc, etc).
While this is the general idea of the View, you are perfectly free to do whatever else you’d like with it. Backbone also doesn’t provide any implementation of these details, other than the triggering/binding of Events, so you’re free to implement what you’d like with the framework(s) of your choosing.
Creating a View Prototype
Let’s cut to the chase and make one!
DonutView = Backbone.View.extend({
tagName : "div",
className : "donut",
render : function() {
this.el.innerHTML = this.model.get('name');
return this;
}
});
I’ve overriden three values in my new prototype. The first two are fairly self-explanatory. When you create the donut view, it will create a div element with the class “donut”.
The last — “render” — is the core function of the Backbone.View. The View itself will create an element, but “render” is where you tell your view how to render itself. Backbone has no opinion on how you do this, so feel free to experiment with one of the dozens of JavaScript template formats that have crept up. (Here’s a cool performance comparison). In this case I set the innerHTML of the element to the content I desire. Pretty straightforward.
var bcDonut = new Donut({
name : "Boston Cream"
});
var bcDonutView = new DonutView({
model : bcDonut
});
var renderedDonutElement = bcDonutView.render().el;
Binding a View to a Model’s Changes
This is a little bit of overhead for just rendering a model’s name. but this extra layer of abstractions gets us a lot. For instance, what if you want to automatically update the view whenever the model changes?
var UpdatingDonutView = DonutView.extend({
initialize : function(options) {
this.render = _.bind(this.render, this);
this.model.bind('change:name', this.render);
}
});
Whoa. Did I blow your mind? First thing we inherited the prototype of DonutView, and overrode its’ “initialize” property with our own function.
Inside initialize we bind this.render to our instance of UpdatingDonutView. The Underscore method ‘bind’ makes sure that when we call “this” in ”render” it will always reference this UpdatingDonutView instance.
Then we bind to the model’s “change:name” event with “render” as the callback function. It’ll now re-render this view when the name changes. Cool!
var uDonutView = new UpdatingDonutView({
model : bcDonut
});
var renderedDonutElement = uDonutView.render().el;
Now, if we change the name:
uDonutView.model.set({name : "Lemon-Filled"});
The element reflects the name change!
renderedDonutElement.innerHTML; -> "Lemon-Filled"
That took a little longer than I’d expected to go over, so we’re going to save collection binding for the next post!
-
Steve
-
http://www.alexrothenberg.com alex rothenberg
-
Johan
-
Johan
-
http://rywalker.com Ry Walker
-
http://mrDarcyMurphy.com Darcy Murphy
-
Taler2003
-
Anonymous
-
http://twitter.com/MattMueller MattMueller
-
Parkerproject
-
Kinakuta James
-
Denis Konchekov
-
Viksit
-
http://twitter.com/customcommander Julien
-
Maer007
-
Anonymous
-
MRush
-
Mark
-
Hugo Monteiro

