Blog

24
Jan

An Intro to Backbone.js: Part 1 – Models and Collections

This is Part 1 of a series of tutorials. You can find Part 2 here.

JavaScript interpreters are FAST, and they’re in every browser out there. You can run a rich application in the browser using only open technologies and built in browser-functionality, today.

Backbone.js can help build it. It’s a light-weight MVC for the browser – only ~1000 lines of code, and it’s clean.

There’s no magic. It’s mostly just a structure for the code you write – not a bulky collection of widgets and doodads. It’s fast to learn, and lets you work with whatever technologies you’re already comfortable with.

Trying Out Backbone

If you’re looking to try it out, you’re going to need to load Underscore.js first. It’s Backbone’s only major dependency.

Oh yeah, and Backbone does not have anything to do with the DOM, although it does have some loose dependencies on the $ and ajax calls of jQuery/Zepto. If you aren’t using jQuery or Zepto then you’ll need to do a little more work (like 10 lines of code). I’ll go over the particulars in the next post. For now, play nice and just use jQuery or Zepto if you feel like following along.

What’s going on in there?

There are five main prototypes in Backbone.

  • Backbone.Collection – A little more than what you’re thinking.
  • Backbone.Model – ActiveRecord implementation, but it’s got a url!
  • Backbone.Controller – More or less exactly what you’re thinking.
  • Backbone.View – If you’re from Rails, then it’s not what you’re thinking.
  • Backbone.Events – Inherit from this guy to give your objects PubSub!

In this blog post we’re going to look at Models, Collections, and Events.

Backbone.Model:

An implementation of the ActiveRecord pattern, but using JSON and a RESTful API instead of directly interfacing with a database.

So let’s say that we’re working on a donut app, and we want to be able to describe our donuts!

  // We extend the Backbone.Model prototype to build our own
  var Donut = Backbone.Model.extend({

    // We can pass it default values.
    defaults : {
      name : null,
      sparkles : false,
      cream_filled : false
    },

    url : function() {
      // Important! It's got to know where to send its REST calls. 
      // In this case, POST to '/donuts' and PUT to '/donuts/:id'
      return this.id ? '/donuts/' + this.id : '/donuts'; 
    } 

  });

That’s analogous to defining a model class in Rails. Now let’s put it through it’s paces a little.

 
// Instantiating
  var bostonCream = new Donut({ // attributes passed to the Donut constructor will override the defaults
    name : "Bostan Cream",
    cream_filled : true
  });

  // Updating and retrieving attributes
  // Actually, let's put sprinkles on that...
  bostonCream.set({ sprinkles : true }); 

  // Saving
  bostonCream.save(); // this will now POST to the RESTful interface.

Backbone.Model#save is an asynchronous method. So you’ll need to provide a callback. The following code assumes success.

  // Now that it's saved it'll have an id..
  bostonCream.id;
  -> 3, or whatever number your JSON api passed up.

  // Wait, what's the name?
  bostonCream.get("name");
  -> "Bostan Cream"

  // Ugh, sprinkles are gross. And there's a typo in the name.
  bostonCream.set({
    sprinkles : false,
    name : "Boston Cream"
  });

  // Updating
  bostonCream.save(); // and now it's a PUT.
  // it will put directly to "donuts/3", since that's the URI
  // of the model now.

Backbone.Collection:

A collection of models, duh. It’s a little smarter than that. It proxies to Underscore to give you a bunch of sweet list functions for working your data out, and lets you keep an eye on what your models are up to through a bunch of handy events. Plus, if you have a RESTful API you can tie them directly to a collection on your backend. Cool, right?

So let’s work out this donut example a little. If you want to manage a collection of donuts, we can use the same API we were working with earlier…

  var Donuts = Backbone.Collection.extend({
    model : Donut,
    url : "/donuts"
  });

  var donuts = new Donuts;

  donuts.fetch(); // this makes a call to the server and populates the collection based on the response.

The call to collection#fetch is asynchronous, so let’s assume this code is being run after the call is complete.

  donuts.at(0); -> gets donuts by index.
  donuts.get(0); -> gets donuts by id.

And a few examples of the Underscore iterator methods…

  donuts.each(function(donut) {
    console.log(donut.get("name"));
  });

  // Select donuts with names longer than 2
  donuts.select(function(donut) {
    return donut.get("name").length > 2;
  });

  // Map...
  donuts.map(function(donut) {
    return donut.get("name");
  });

Nested Collections

One of the most common uses for collections is to make them nested. In this case, let’s introduce another model, the “Donut Shop” and let’s say that it has a list of Donuts that it serves.

  var DonutShop = Backbone.Model.extend({
    defaults : {
      name : "Untitled"
    },

    initialize : function() {
      // When you extend a Backbone.Model and give it an initialize function,
      // the function is called when you instantiate an instance of your Model.

      // The initialize function is used repeatedly in Backbone's prototypes. We'll be seeing this again
      this.donuts = new Donuts;
      this.donuts.url = 'donut_shops/' + this.id + "/donuts";
    }
  });

So now if you have an existing donut shop that has been saved to the server, you can go:

  donutShop.donuts.fetch();

Backbone.Events:

All of the Backbone core objects give you events to plug in to. For instance, the Collection prototype allows you to bind to “add” and “remove” events.

  donutShop.donuts.bind("add", function(donut) { 
    alert("added " + donut.get("name")); 
  });

  // now adding a donut will trigger the alert
  var lemonFilled = donutShop.donuts.add({ name : "Lemon Filled" }); 

Backbone provides you with the Backbone.Events object as well. So, since Underscore is a prereq of Backbone anyway, you can do fancy nonsense like this…

var application = {};
_(application).extend(Backbone.Events);

// Now your application object has it's own events! For example.

application.bind("fun:had", function() { alert("wee!"); });
application.trigger("fun:had"); // it'll alert "wee!"

There’s part 1. Next time I’ll take you through some fun parts:

  • Navigating using Backbone.Controllers
  • HashChange routing!
  • Creating a view
  • Tying a view to model events
  • Tying a view to a collection’s events.

If you’re looking to try out Backbone, which I’m guessing you are, here are the relevant links.


  • frytaz

    Good post, I’m going to use backbone.js on my next project! :)

  • Jeremy

    Terrific introduction — thanks! Slight typo in the second code block showing the examples.

    // Wait, what’s the name?
    bostonCream.get(“name”);
    -> “Boston Cream”

    should that be

    // Wait, what’s the name?
    bostonCream.get(“name”);
    -> “Bostan Cream”

    since it hasn’t been updated yet?

  • n_time

    Nice catch! Thanks for the feedback!

  • Pingback: An Intro to Backbone.js: Part 1 – Models and Collections – Liquid Media « Netcrema – creme de la social news via digg + delicious + stumpleupon + reddit

  • Pingback: links for 2011-01-25 | toshism

  • Adam

    Great post, can’t wait for the next one on backbone!

  • caner

    thanks for the article but can you please change the font color of your code example divs. white on black background just f’d my eyes.

  • Johan

    Just starting out with backbone.js and enjoyed your nice examples and explanation. Looking forward to the next post.

  • http://pomodoro.nl pomodoro

    Nice article looking forward to the next one. I’m just finishing my first project using backbone and am really enthusiastic. Especially with the clarity of the framework and great support on irc.

  • Pingback: Pedro Newsletter 28.01.2011 « Pragmatic Programmer Issues – pietrowski.info

  • http://www.redstone.eu Greg

    Great introduction, thanks! A little curios about the first code snippet; “url : “/donuts” // Important!” for the Donut model. Setting this to a hard coded value will not work, will it? In your second snippet, the update will PUT to “/donuts” since the url attribute will be used.

    I think the default behavior of Backbone, given that no url attribute (which may be a function) is supplied on the model, is to use the collection url and add the ID if the model has been saved before. Setting the url on the Donut model will override this behavior and will not automatically append the ID.

    Looking forward to the next part, Cheers!

    • n_time

      Hi Greg,

      Thanks for the feedback. You’re totally right.

      It’s possible to give url a function as well, so I’ve now moved the proper behavior into the model. Nice catch!

      In most of our projects we’ve used .add on Collections, so I hadn’t realized that was a misconception. Good thing!

  • Pingback: JavaScript Magazine Blog for JSMag » Blog Archive » News roundup: the performance of feature detection, Mobile Firefox 4 performance, W3C Touch Events

  • http://twitter.com/MattMueller MattMueller

    Great introduction! It’s worth noting that this example does not work – bostonCream.save() is an asynchronous event and will not return an id in time for bostonCream.id. Mentioning that would have saved me some time, so it may be useful to add that for others.

    One other helpful tip is elaborating on what the “json api” returns back. You need to pass back JSON that includes { id: someID …. }

    Thanks again!

    • Anonymous

      Hey man! Thanks for the feedback! I’ve included mention of this above. ;)

    • http://profiles.yahoo.com/u/V7LGTBIAONDXFOC4A7LRH6LD7U George

      The parse method of the model can be overridden to handle JSON that is not exactly formatted the way JSON needs. For example, I work with a legacy api and all the data I need comes back in a nested property called data. In the parse method I can just do the following:

      parse: function (response)
      {
      return {
      id: response.data.id,
      version: response.data.version,
      name: response.data.name,
      key: response.data.key,
      etc..
      };

      },

    • iJames

      Your comment about JSON needint {id: something…} is the most crucial thing I’ve seen anywhere regarding backbone.  The concept of backbone is awesome but I have spent way too long trying to decipher exactly what the JSON can look like and how to render that…  Thanks for this little winner!!!

  • Pingback: Quora

  • Steveshamen

    This example is complicated.

    Where is the start, middle and end. Currently all I see is the middle. Where is the page load initiator? Ie document onready/ window.onload etc..

    What does the default html and end result look like on the page?

    I can assume only so much when all I’m looking at is clever object structures extended using an unknown library (backbone) with jquery.

    I am stupid. Don’t miss out the essential. I want to see the most basic of examples working and examples of why this id superior to say doing?

    //JS ###################
    //includes/depends: jquery, backbone etc…

    //libary
    function parse(xml){
    //parse xml etc…
    return xml;
    }

    //onload
    $(function(){
    $.ajax(url,function(xml){
    $(‘#liveupdate’).html(parse(xml));
    })
    })

    //DEFAULT HTML #############################
    loading…

    //RESULT HTML ###############################
    content from the url has been loaded into this div.

  • Pingback: Kickass Labs » Blog Archive » Yet Another Backbone.js Tutorial – Part 1 – Backbone.js Philosophy

  • Diegocastorina
  • Jacek

    Links for other two parts are needed… Now only place to find those is google.
    Very good tutorial though (all 3 parts)

  • Pingback: jQuery & Backbone.js | IT Zen

  • Stephen

    first found this set of tutorials 5 months ago, and found it to be a great introduction, but I haven’t really had need to leverage backbone in the interim.  Now i’m diving into it much more deeply and I’ve once again come back to this tutorial!  The nested collection, in particular the method for supporting its url was EXACTLY what I was looking for confirmation on.

    THANK YOU!

  • yun_cn

    if you had added  executable examples that might help me a lot as an introduction.

  • Max

    It would be great if you could tie up some loose ends in this tutorial. Where is the “Donut” class created? You new it, but you don’t seem to define it anywhere.

  • Pingback: Loccasions: Going Client-Side with Leaflet, Backbone, and Jasmine » RubySource

  • Murali Rama Krishnan

    donuts.pluck(‘name’)easier way to map the collections

  • Jiggaboo

    Very bad tutorial. You should delete it to not waste time of people that try to learn Backbone.

  • http://twitter.com/davearel David Arel

    FYI, you have “sparkles” instead of “sprinkles” within your defaults object.

  • Prajwol

    Indeed, “This example is complicated.”
    If only you could provide us the executable files with the possible outcomes. Im finding it hard to even start.
    Cud you please explain about that url part like what does it do and why we need it?

  • efitcecil

    Nice. I like this

  • Landon Sanders

    wow!

  • Shoaib

    Hey!
    Any source code?

  • Yuwen Song

    thanks for the clear toturial