Delivering solid user friendly software solutions since the dawn of time.

Backbone.js Fundamentals - Notes

Categories: Javascript Notes

Liam McLennan

I like the model inheritance and statics of backbone. It is also really nice that you can replace the server-side with a local storage implementation. I do prefer the model binding of knockout however. Just seems more clean then the makes.

1. Introduction
Backbone.js gives structure to web applications by providing models with key-value binding and custom events, collections with a rich API of enumerable functions, views with declarative event handling, and connects it all to your existing API over a REstful JSON interface.
It's not a framework and it is not MVC.
Pros: Fast, Highly interactive, Scalable
Cons: Extra work for SEO, Difficult to test, Security issues
Server side pages -> Hybrid Ajax pages -> SPAs client side
Task board SPA - http://trello.com
Required Dependencies
  • Underscore.js
  • jQuery or zepto
  • Zepto lightweight jquery compatible api used for mobile devices. - http://zeptojs.com/  
var book = new Backbone.Model({title:'white tiger', author:'Aravid'});
book.get('title'); // "White tiger"
book.set('title', 'Funny tiger');
http://jsfiddle.net/ynkJE/ - preconfigured for backbone testing
(function() {
var Rectangle = Backbone.Model.extend({});
var RectangleView = Backbone.View.extend({
tagName: 'div',
className: 'rectangle',
events: {
'click': 'move'
render: function() {
return this; //backbone convention
setDimensions: function() {
width: this.model.get('width') + 'px',
height: this.model.get('height') + 'px'
setPosition: function() {
var position = this.model.get('position');
left: position.x,
right: position.y
move: function() {
this.$el.css('left', this.$el.position().left + 10);
var myRectangle = new Rectangle({
width:100, height:60, position: { x:300, y:150 }
var myView = new RectangleView({ model: myRectangle });
//when creating an array
//_(models).each(function(model) {
//$('div#canvas').append(new RectangleView({model:model}).render().el);
2. Models
var Vehicle = Backbone.Model.extend(
defaults: {
'color': 'white',
'type': 'car'
initialize: function() {
console.log('this is a contructor');
summary: function() {return 'a static function';}
var car = new Vehicle(); //will run constuctor
var SuperCar = Vehicle.extend({});
var superCar = new SuperCar();
console.log(superCar instanceof SuperCar); //true
console.log(superCar instanceof Vehicle); //true
console.log(superCar instanceof Backbone.Model); //true
'maximumSpeed': '99',
'description', '<script>injection</script>'
ford.has('type'); //check if attribute defined 
Models raise events when their state changes, detect by listening to change event.
ford.on('change', function () {}); //listen to change of object
ford.on('change:color', function () {}); //listen to change of property
Custom model events are identified by string identifiers.
var volcano = _.extend({}, Backbone.Events);
volcano.on('disaster:eruption', function(options){
console.log('duck and cover - ' + options.plan);
}); //namespacing events
volcano.trigger('disaster:erruption', {plan:'run'});
var ford = new Backbone.Model({});
console.log(ford.id) //undefined, because not saved to server yet
console.log(ford.cid) //c0, temp id
console.log(ford.isNew()); //true
Validation is called prior to set and save operations.
var Vehicle = Backbone.Model.extend(
validate: function (attrs) {
var validColors = ['white','red'];
var colorIsValid = function (attrs)
if (!attrs.color) return;
return _(validColors).include(attrs.color); 
if (!colorIsValid(attrs)) {
return "color must be one of: " + validColors.join(",");
var car = new Vehicle();
car.on('error', function (model,error){
car.set({'color','green'}, {
error: function (error) {
var attrs = ford.toJSON(); //converts a model's attributes to a javascript object
console.log(JSON.stringify(attrs)); //string representation
  • DOM - View - Model : glue between models and documents
var V = Backbone.View.extend({
attributes: {
'data-value': 12345
var v = new V();
var V = Backbone.View.extend({});
var v = new V({el: '#test'});
var v = new Backbone.View({el:'body'});
v.el // <body></body> the dom element
v.$el // cached $(this.el) [<body></body>] the jquery wrapper of that element
this.$ // this.$('selector') = this.$el.find('selector') scoped to current view 
var RefreshingView = Backbone.View.extend({
initialize:function() {
this.model.on('change', function() { this.render(); }, this);
render: function() {
var m = new Backbone.Model({text:new Date().toString()});
var v = new RefreshingView({model:m, el:'body'});
setInterval(function() { m.set({text:new Date().toString());},1000);
//<h3 class="not-imortant">first version</h3>
var el = new Backbone.View().make('h3', { class: 'not-important' } , 'first version'); 
v.remove() //$el.remove();  to remove view from DOM
var FormView = Backbone.View.extend({
events: {
'click .clickable': 'handleClick'
handleClick: function() {}
  • Do not attach to existing elements
  • Do not access DOM elements the view does not own
  • Pass el to the constructor of self-updating view
4. Templating
Client-side Templating
<% ... %> //exececute arbitrary code 
<%= ... %> //evaluate an expression and render the result inline
<%- ... %> //evaluate an expression and render the html escaped result inline
<script id="latlon-template" type="text/html">
<%=lat%> <%=lon%>
<% ([1,2,3]).each(function(number) {%>
<% }); %>
var V = Backbone.View.Extend({
render: function() {
var data = { lat:-27, lon=123 };
var template = $('#latlon-template').html();
return this;
var v = new V({el:'body'});
var compiled = _.template(source);
 Handlebars is templating engine based on mustache. (no code philosophy)
{{lat}} {{lon}}
{{#each numbers}}
var compiled = Handlebars.compile(source);
var rendered = compiled({lat:-27}); 
Server-side Templating
handlebars <input> -f <output> //pre-compile file based templates
var rendered = Handlebars.templates.list(data);
5. Routing 
  • Don't use routing like mvc controllers!
var DocumentRouter = Backbone.Router.extend({
routes: {
'contents' : 'contents'
'view/:title': 'viewDocument'
contents: function(){
$('body').html(new ContentsView({collection:documents}).render().el);
viewDocument: function (title) {
var selectedDocument = _(documents).find(function (document) {
return document.get('title') == title;
$('body').empty().append(new DocumentView({model: selectedDocument}).render().el);
var router = new DocumentRouter();
router.navigate('contents', {trigger:true});
eventAggregator.on('document:selected', function(document){
var urlPath = 'view/' + document.get('title');
router.navigate(urlPath, {trigger:true});
window.history.pushState(...); //html5 history api, else backbone will use hash fragments
http://localhost/search/cats vs http://localhost/#search/cats
  1. Render content on the server
  2. #! Urls 
router.navigate('!search/cats', {trigger:true});
// generated url -> /#!search/cats
// google converts to -> /_escaped_fragment_=search/cats which will need server rendered content 
6. Collections 
  • Container for multiple models of the same type
  • Retrieve models from the server
  • Create models and save them to the server
  • Group models by some attribute
  • Collection is an array-like object (can't use square brackets)
var c = new Backbone.Collection([
console.log(c.length); //2
console.log(c.at(0)); // thing
var Vehicles = Backbone.Collection.extend({
comparator: function(vehicle) {
return vehicle.get('sequence');
comparator: function(vehicle1, vehicle2) {
return vehicle1.get('sequence') < vehicle2.get('sequence') ? -1 : 1;
var vehicles = new Vehicles(...);
vehicles.add({name:'Fred', age:6}); //will convert to backbone model
vehicles.on('add', function (model, col, options) { ... options.index ... };
vehicles.add({name:'Fred2', age:6}, {at:2, silent:true}); 
var v1 = vehicles.get(1);
var v2 = vehicles.getByCid('c0');
//proxy from underscore.js
collection.forEach(function (item { print(item); });
collection.map(function(item) { return transform(item); });
collection.reduce(function(memo,item) { return memo + item.get('age'); }, 0 );
var dave = collection.find(function(model) { return model.get('name') == 'Dave'; });
collection.on('remove', function(model, collection) {});
collection.on('change:name', function(model, options) {});
7. Connecting to a Server
  • CORS - Cross-origin resource sharing 
  • Origin is application layer protocol + domain name + port number
  • Cors is an alternative to jsonp (jsonp only has GET verb)
  • Cors supports all verbs but needs modern browser (IE10)
var MyModel = Backbone.Model.extend({
url: 'http://tra.la/mymodel'
myModelInstance.save({}, {
success: function() {}, //response will give id
error: function() {}
https://github.com/liammclennan/backbone-server (with node.js)
people.create({name:"Tom", age:50}); //http post to insert a model
people.fetch() //get array of models that must have an id
var person = new Person({id:0});
people.add(person); //will get url from the collection
person.fetch({ success: function() {...} });
  • A function that interfaces between backbone and the server
  • Implements crud behavior and can be overridden globally, per collection, or per model
  • Sets backbone.sync to local browser storage
8. Testing with Jasmine (instead of Mocha or qUnit)
  • Test model specifications
  • Test views (rendered elements, raised events)
  • Testing routes (difficult, so keep routing easy)
rectangle = new app.Rectangle();
describe('some context', function(){
it('should show some observable behavior', function() {
//assert expectations here
expect(mySetFunction).toThrow(); // will give error
Testing without a browser
  • For QUnit you can use http://phantomjs.org/ 
  • (you can also use this to take screenshots)
  • phantomjs.exe run-jasmine-fixed.js SpecRunner.html //0 success 1 failure