BackboneJS

Step by Logical Step

By Nicholas Johnson

Version 0.51 Beta

What is this, what is that?

Backbone is a the grand-daddy of all the JavaScript MVC frameworks. It's the progenitor of frameworks like Ember, Angular, even Meteor. It's a sensibly put together library full of tools to help you organise your web application, a toolkit for tidy JavaScript.

You can use it to enhance your existing site, to add interactivity to a form for example, or you can go all out and create a single page app (SPA), where the entire site is generated clientside using JavaScript.

Backbone comes packaged as a single JavaScript file which you link in the header of your page. It's purely clientside, and says very little about your server. Your server should ideally expose a restful JSON API, but this is not a requirement.

Backbone engages in no magic, is very readable, and doesn't try to do too much for you.

Architecture

Backbone provides you with a Model View (MV) architecture. Unlike other JavaScript frameworks, there are no controllers. The view works a little like a controller.

Models

Your model is where you store your data. If you have an app for tracking sharks in the ocean, you might have a Shark model which would store the location and name of a shark.

Models fire off events when they change. If your shark eats a swimmer and gains 10 notoriety points, it will fire an event to that effect.

Views

A view in Backbone is a region on the page into which Backbone will output some data. A view might be a form, or a map of sharks in the sea. Your page will be built up of one or more views.

Views can listen to model events and redraw themselves when the model they are attached to changes.

Collections

A Backbone collection is an array of models. If you have 100 sharks, you would store those in a collection. If you tag a new shark, you might add it to the collection.

Collections can also fire events. A view can listen to a collection.

Router

The Backbone Router uses JavaScript pushstate to change the URL in the address bar, allowing you to create an entire website clientside without hitting the server.

Getting started

Backbone depends on:

Underscore

an excellent library of useful JavaScript functions, including functions for functional style array and object manipulation, plus genuinely useful utility methods.

Download here: http://underscorejs.org/

JQuery

Everyone's favourite DOM mainpulation tool. Choose 1.x if you want to support legacy browsers. Choose 2.x if you want a smaller faster codebase.

Download here: http://jquery.com/

Boilerplate

A super stripped down Backbone boilerplate for local development might look something like this:

<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="underscore.js"></script>
<script src="jquery.js"></script>
<script src="backbone.js"></script>
<!-- your code goes in app.js -->
<script src="app.js"></script>
</head>
<body>
</body>
</html>

Content Delivery Network (CDN)

You might also serve the libraries directly from a Content Delivery Network (CDN). Here are some options:

If you serve from a CDN you will need to be connected to the internet to work on your site.

Getting Started with Backbone Models

A model in Backbone is where we store our data. We create our model by inheriting from (extending) Backbone.Model.

Extend is an underscore function that simply creates a new object using the provided object as a prototype.

Shark = Backbone.Model.extend();
var shark = new Shark();

Initialising the model

We can initialise our model with some values by passing in a JSON object, like so:

var shark = new Shark({
latitude: 45,
longitude:38,
name: 'Theophilus T. Shark'
});

Setting attributes on the model

We can set attributes on our model using the set function.

shark.set('name', 'Lennie');

We can also set multiple attributes at the same time:

shark.set({
name: 'Lennie',
age: 9.5
});

Getting values from the model

We can get values back out of the model using the get function:

shark.get('name');

Why Do We Need .set & .get Functions?

We typically set and get attributes on a JavaScript object using the dot notation, like this:

shark.name = 'Lennie';
console.log(shark.name); // Outputs 'Lennie'

With Backbone we set and get attributes using .set and .get, like this.

shark.set('name', 'Lennie');
console.log(shark.get('name')); // Outputs 'Lennie'

So why use these functions? The set function does 3 things:

  1. It sets the value as an attribute of the attributes object in your model, avoiding namespace collisions with functions defined by the Model prototype.
  2. It triggers a 'change' event on the model. Our views can listen out for the change event and optionally redraw themselves.
  3. It triggers a 'change' event on any collection that the model is part of. Again, any Views listening to the collection will be notified.

Using .set allows us to trigger events and wire our app together using listeners. This prevents spaghettification.

Using .get allows us to pull attributes from the shark.attributes object without our code explicitly referencing it.

More with Models

When we first define our shark, we can pass in an object which will define it. For example:

Setting defaults

We can set default values for our shark by passing in a defaults object:

var Shark = Backbone.Model.extend({
defaults: {
name: 'Mr Shark',
age: 0
}
});
var shark = new Shark();
alert(shark.get('name')); // Outputs "Mr Shark"

Model Events

We can wire up our model to listen to change events and respond. We can pass in an initialize function, and wire our events up here:

var Shark = Backbone.Model.extend({
initialize: function () {
this.on("change:name", function (model) {
alert("Shark is now called " + model.get("name"));
});
}
});
var shark = new Shark();
shark.set('name', 'Davey'); // alerts "Shark is now called Davey"

Here we have told our model to listen to itself. When it's name is changed, it calls a function which alerts the new name. More commonly models would listen to other models, or views would listen to models.

Events are identified by a String

Just like jQuery, Events are identified by a unique string. In the example above we were listening for: "change:name". Our model can fire lots of different types of events.

Change Event

The change event is fired whenever any of the model's attributes change. This is useful if we just want to detect any change in the model's attributes.

shark.on('change', function() {
alert("attribute changed")
})

Change:[attribute] Event

This event is fired whenever a specific attribute change, for example "change:name" or "change:age". Use this if you want to listen to a specific attribute.

shark.on('change:name', function() {
alert("name changed")
})

All

The all event is always fired whenever any other event is fired. This could be a network event, the model could be added or removed from a collection, an attribute could have been changed, anything. If we are interested in any type of event on our model, we can just listen for the all event.

shark.on('all', function() {
alert("some event occurred to shark")
})

Further reading

You can view the complete list here:

http://backbonejs.org/#Events-catalog

Views

A Backbone View is an object which controls a part of your web page. It's linked to a DOM node which can either be on the page, or a fragment, not connected to the page.

Your backbone view will typically have a render method which will redraw the view. You can call this method yourself, or you can set up listeners to track a model and call render when it changes.

A simple view looks like this:

var SharkTankView = Backbone.View.extend({
initialize: function () {
this.render();
},
render: function () {
this.$el.append('sharks!');
}
});

This sets up a SharkTankView prototype, effectively a class which you can inherit from to create SharkTankView objects. The render function simply uses jQuery to append the string 'Sharks!' to the $el object, which is a jQuery object. The initialize function simply calls render.

In order to use your SharkTank, you need to create an object that inherits from it:

$(function () {
var sharkTankView = new SharkTankView({
el: $('#tank')
});
});

You would then need to hook this up to a HTML page with an element where id="tank", like this. Your code will go in app.js

<!DOCTYPE html>
<html>
<head>
<title>Hello</title>
<script type="text/javascript" src="underscore.js"></script>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="backbone.js"></script>
<script type="text/javascript" src="app.js"></script>
</head>
<body>
<div id="tank"></div>
</body>
</html>

View Events

Views are responsible for listening out for events that happen in the region of the DOM they control.

Events are specified in a JSON object, and are written in the form: {"event selector": "callback"}

Selectors are scoped to the DOM node controlled by the view, so if you have a lot of shark tanks on the page, you don't have to worry about them conflicting with each other.

$(function () {
var SharkTankView = Backbone.View.extend({
initialize: function () {
this.render();
},
events: {
"click button": "dunk"
},
dunk: function () {
alert('Oh dear, my legs have been bitten off.');
}
});
var sharkTankView = new SharkTankView({
el: $('#tank'),
});
});

and the html

<!DOCTYPE html>
<html>
<head>
<title>Hello</title>
<script type="text/javascript" src="underscore.js"></script>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="backbone.js"></script>
<script type="text/javascript" src="hello.js"></script>
</head>
<body>
<button>This button does nothing</button>
<div id="tank">
<button>Click to Dunk</button>
</div>
</body>
</html>

Notice that the first button does nothing because it it outside of the tank element. Backbone will only look for elements within the view's el.

The events attribute is just a shorthand

Using the events attribute to set up our view events is just a convenient shorthand. We can just as well set up events in the initialize function, like so:

var SharkTankView = Backbone.View.extend({
initialize: function () {
this.$('button').click(this.dunk);
},
dunk: function () {
alert('Oh dear, my legs have been bitten off.');
}
});

This is acceptable, but arguably less readable. You should favour the events attribute for listening to the DOM.

Templates

Packing your render methods full of jQuery rapidly becomes yawnsome. Templates are a way of helping you to write less HTML generation code by compiling strings.

Backbone does not come with a templating language of it's own, but is designed to work well with underscore templates.

Generating a template

We can generate a template by passing a string to the _.template function. This will return a function which we can call to generate an HTML string, like so:

var template = _.template("<p>Hello!</p>");
template();
// outputs "<p>Hello!</p>"

Let's use our template to generate a view:

$(function () {
var template = _.template("<p>Hello World!</p>");
var SharkTankView = Backbone.View.extend({
initialize: function () {
this.render();
},
render: function () {
this.$el.html(template);
}
});
new SharkTankView({
el: $('#tank'),
});
});

Note that we don't need to call the template. If this.$el.html receives a function instead of a string, it will call the function for us.

Template includes

We can include variables in our templates using erb style syntax. We can then generate a string by calling the template function and passing an object, like so:

var template = _.template("<p>Hello <%= name %>!</p>");
template({name: "Wavy Davey"})
// generates the string "<p>Hello Wavy Davey!</p>"

You need to make sure you pass through all attributes or you will get an error.

Further reading

Read more about underscore templates here:

http://underscorejs.org/#template

Extracting Templates from your HTML

Creating templates as strings in JavaScript is OK, provided our templates are very small, but what if they become larger? It would be much nicer if we could define templates directly in our HTML, and of course we can.

We compose the template into our HTML page, then we just use jQuery to pull it out as a string which we can then pass to _.template.

The convention is to put the template in a script tag with type="text/template". Content between script tags will not be rendered to the page, but can still be accessed using jQuery.

See below:

<!DOCTYPE html>
<html>
<head>
<title>Hello</title>
<script src="../../underscore.js"></script>
<script src="../../jquery.js"></script>
<script src="../../backbone.js"></script>
<script src="hello.js"></script>
</head>
<body>
<div id="tank"></div>
<!-- And here is the template -->
<script type="text/template" id="tank-template">
<p>Swim Away <%= name %></p>
</script>
</body>
</html>

We then pull this content as a string using a simple jQuery selector, and store it as a template function in the tankTemplate attribute.

Then in our render function we can simply call the template, passing in any expected values.

var SharkTankView = Backbone.View.extend({
initialize: function () {
this.render();
},
tankTemplate: _.template(
$('#tank-template').html()
),
render: function () {
this.$el.html(this.tankTemplate({
name: "Danger Mouse McGree"
}));
}
});

Further reading

The Backbone sample in MVCTodo gives a very clear demo of how to build a simple todo app.

https://github.com/tastejs/todomvc/tree/gh-pages/architecture-examples/backbone

comments powered by Disqus

Do you like Email? Do You Like JavaScript?

Sign up for occasional emails covering modern Web Development topics such as Angular 2 and React. You can unsubscribe at any time.

We totally respect your email privacy

comments powered by Disqus