Backbone.js Wine Cellar Tutorial — Part 1: Getting Started

One of the challenges when building nontrivial Web applications is that JavaScript’s non-directive nature can initially lead to a lack of structure in your code, or in other words, a lack of… backbone. JavaScript is often written as a litany of free-hanging and unrelated blocks of code, and it doesn’t take long before it becomes hard to make sense of the logic and organization of your own code.

Backbone.js is a lightweight framework that addresses this issue by adding structure to JavaScript-heavy Web applications.

Self-contained building blocks

Backbone.js provides several classes (Model, Collection, View, Router) that you can extend to define the building blocks of your application. To build an app with Backbone.js, you first create the Models, Collections, and Views of your application. You then bring these components to life by defining a “Router” that provides the entry points of your application through a set of (deep-linkable) URLs.

With Backbone.js, your code is organized in self-contained entities (Models, Collections, Views): No more free-hanging and unrelated blocks of code.

Data Binding

With Backbone.js, you bind Views to Models so that when a Model’s data changes, all the Views bound to that Model automatically re-render. No more complex UI synchronization code.

Elegant REST Integration

Backbone.js also provides a natural / magical / elegant integration with RESTful services. If your back-end data is exposed through a pure RESTful API, retrieving (GET), creating (POST), updating (PUT), and deleting (DELETE) models is incredibly easy using the Backbone.js simple Model API.

Sample Application

In this three-part tutorial, you’ll create a Wine Cellar application. You can browse through a list of wines, as well as add, update, and delete wines.

  • In Part 1 (this post), you define the basic infrastructure. You create a “read-only” version of the application: you’ll be able to retrieve a list of wine and get the details of each wine.
  • In Part 2, you add the code to add, update and delete wines. You leverage Backbone’s powerful REST integration.
  • In Part 3, you add complete support for history management and deep linking.


NOTE: I also blogged a non-Backbone version of the application here (Java back-end) and here (PHP back-end), which you can look at for comparison.

Part 1: The Read-Only Wine Cellar Application

You can run the application (Part 1) here.

Here is the code:

// Models
window.Wine = Backbone.Model.extend();

window.WineCollection = Backbone.Collection.extend({
    model:Wine,
    url:"../api/wines"
});

// Views
window.WineListView = Backbone.View.extend({

    tagName:'ul',

    initialize:function () {
        this.model.bind("reset", this.render, this);
    },

    render:function (eventName) {
        _.each(this.model.models, function (wine) {
            $(this.el).append(new WineListItemView({model:wine}).render().el);
        }, this);
        return this;
    }

});

window.WineListItemView = Backbone.View.extend({

    tagName:"li",

    template:_.template($('#tpl-wine-list-item').html()),

    render:function (eventName) {
        $(this.el).html(this.template(this.model.toJSON()));
        return this;
    }

});

window.WineView = Backbone.View.extend({

    template:_.template($('#tpl-wine-details').html()),

    render:function (eventName) {
        $(this.el).html(this.template(this.model.toJSON()));
        return this;
    }

});

// Router
var AppRouter = Backbone.Router.extend({

    routes:{
        "":"list",
        "wines/:id":"wineDetails"
    },

    list:function () {
        this.wineList = new WineCollection();
        this.wineListView = new WineListView({model:this.wineList});
        this.wineList.fetch();
        $('#sidebar').html(this.wineListView.render().el);
    },

    wineDetails:function (id) {
        this.wine = this.wineList.get(id);
        this.wineView = new WineView({model:this.wine});
        $('#content').html(this.wineView.render().el);
    }
});

var app = new AppRouter();
Backbone.history.start();

Code Highlights:

  1. WineModel (line 2): Notice that we don’t need to explicitly define the attributes (name, country, year, etc). You could add validation, default values, etc. More on that in Part 2.
  2. WineCollection (lines 4 to 7): “model” indicates the nature of the collection. “url” provides the endpoint for the RESTFul API. This is all that’s needed to retrieve, create, update, and delete wines with Backbone’s simple Model API.
  3. WineListView (lines 10 to 25): The render() function iterates through the collection, instantiates a WineListItemView for each wine in the collection, and adds it to the wineList.
  4. WineListItemView (lines 27 to 38): The render() function merges the model data into the “wine-list-item” template (defined in index.html). By defining a separate View for list items, you will make it easy to update (re-render) a specific list item when the backing model changes without re-rendering the entire list. More on that in Part 2.
  5. WineView (lines 40 to 49): The view responsible for displaying the wine details in the Wine form. The render() function merges the model data (a specific wine) into the “wine-details” template retrieved from index.html.
  6. AppRouter (lines 52 to 71): Provides the entry points for the application through a set of (deep-linkable) URLs. Two routes are defined: The default route (“”) displays the list of wine. The “wines/:id” route displays the details of a specific wine in the wine form. Note that in Part 1, this route is not deep-linkable. You have to start the application with the default route and then select a specific wine. In Part 3, you will make sure you can deep-link to a specific wine.

Download

The source code for this application is hosted on GitHub here. And here is a quick link to the download.

You will need the RESTful services to run this application. A PHP version (using the Slim framework) is available as part of the download.

UPDATE (1/11/2012): A version of this application with a Java back-end (using JAX-RS and Jersey) is also available on GitHub here. You can find more information on the Java version of this application here.

Part 2 is available here.

Comments

  1. Just an FYI, when I click to view a wine I end up with this in the URL:

    http://www.coenraets.org/backbone-cellar/part1/#wines/11

    If I cut and paste that into a new tab, it is throwing an error and not loading the wine. (Well, in console anyway ;)

  2. Christophe says:

    Ray,
    As mentioned in the post, this is the normal behavior in Part 1. Part 3 of this tutorial covers deep linking.
    Christophe

  3. Doh! Helps if I read closer. :)

  4. José Adenaldo Bittencourt says:

    Where are the adobe flex posts?

  5. JCLang says:

    Hello Christophe,
    thanks, your blog is top quality.
    I hope you’ll focus back to Flex soon! :)

  6. Jeremy says:

    I will definitely try this one. My prefered client side implementation of those posts

  7. Christophe,
    thanks for this tutorial. It would be great of Ray could add the CF backend to this :-)

    What IDE do you use or recommend for JS development?

    Regards,

    Stefan

    • Robin says:

      Aptana is a great IDE for front-end development. Back-end too but it supports javascript code completion very well.

  8. Dimitris says:

    Great tutorial.
    I’ve read only Ruby on Rails RESTful services.
    php approach is great
    I am waiting for part 2.

    Dimitris
    from Thessaloniki Greece

  9. Christophe Coenraets says:

    @jose, @JCLang… Thanks. I have a couple a Flex posts lined up. Stay tuned.

  10. Christophe Coenraets says:

    @Stefan. Still using TextMate for now.

  11. what if your are not using a restful service to connect?

  12. cole\ says:

    Great tutorial. really helped me a lot.
    I noticed in your demo every time you choose a wine and then hit the back button you are making another call for data. any thoughts? I added code that checks for the collection b4 calling fetch. Am i missing something?

    thanks again for this great work!!!

  13. Hecky says:

    The link to the part 2 of the tutorial seems broken, ;)

  14. Christophe says:

    @Hecky: Thanks. Fixed.

  15. Seth Stone says:

    Thanks for the great post; I’m really enjoying the topics you’re working through. On this app I’m having a bit of trouble with the PHP. Do you happen to know if there’s a min version (of PHP) required to run this? I have 5.1.6 and I’m getting: PHP Fatal error: Uncaught exception ‘ErrorException’ with message ‘setcookie() expects at most 6 parameters, 7 given’. Also, since I’m not too interested in the server side at this point is there a way I could point the JS (served from my server) to the REST API hosted on your system?

    • Seth Stone says:

      In case anyone runs into the issues I did… for this to work I had to have a version of PHP higher than 5.1 (5.3.3 worked for me when 5.1.6 did not). Apache has to be configured to AllowOverride FileInfo for the api directories, as this is necessary for .htaccess files to be processed which Slim.php depends on. On RHEL also had to have the php-mysql package installed. Thanks!

      • Faz says:

        Thanks for the heads up Seth. Was facing this issue on the Twitter Bootstrap+Backbone tutorial and found your suggestions. Working fine now.

  16. Justin says:

    Like it and as other post indicated I really think there should be a version with CF back end.

    regards
    J

  17. Benjamin says:

    I think it is a bad idea to present an introductory tutorial like this and teach people to pollute the global namespace.

    There are better ways of handling that situation.

  18. jbcavarec says:

    Hello,
    every time i use fetch() i need to wait for complete download of datas before doing templatinf for example.
    You seem not need to wait for asynchronous call in your example:
    this.wineList.fetch();
    $(‘#sidebar’).html(this.wineListView.render().el);
    how is it possible ?
    thank you

Trackbacks

  1. [...] Part 1 of this tutorial, we set up the basic infrastructure for the Wine Cellar application. The [...]

  2. [...] here: Backbone.js Wine Cellar Tutorial — Part 1: Getting Started Share and [...]

  3. [...] Backbone.js Wine Cellar Tutorial — Part 1: Getting Started. Share this:TwitterFacebookLike this:LikeBe the first to like this post. [...]

  4. LARGE SCALE APPLICATION DEVELOPMENT…

    LARGE SCALE APPLICATION DEVELOPMENT Backbone.js:…

  5. [...] Part 1 of this tutorial, we set up the basic infrastructure for the Wine Cellar application. In Part 2, we [...]

  6. [...] follow-up posts, “Backbone.js Wine Cellar Tutorial” (part1, part 2, part 3), I showed how to add structure to the client-side of the Wine Cellar application [...]

  7. [...] follow-up posts, “Backbone.js Wine Cellar Tutorial” (part1, part 2, part 3), I showed how to add structure to the client-side of the Wine Cellar application [...]

  8. [...] few weeks ago, I posted a three-part Backbone.js tutorial (part 1, part 2, part 3). Since then, I spent more time building a real-life application with Backbone. I [...]

  9. [...] I blogged a three-part Backbone.js tutorial (part 1, part 2, part 3), a number of people asked me to try Angular.js. So I decided to take it for a test [...]

  10. [...] recently blogged a tutorial (part 1, part 2, part 3, and postface) that takes you through the process of building a CRUD application [...]

  11. [...] Adobe evangelist Christophe Coenraets recently posted a three part article on building a CRUD application using HTML and the Backbone.js framework. He [...]

  12. [...] you are new to Backbone.js, you may want to start with the tutorial (part 1, part 2, part 3, and postface) I blogged recently. “Backbone Directory” includes some [...]

  13. [...] Bookmarked Backbone.js Wine Cellar Tutorial — Part 1: Getting Started [...]

  14. Javascript school…

    Javascript school This page is a resource for javascript development at TheLadders. It will host javascript content and examples for writing code at TheLadders. Conventions See Front End Coding Guideline Links Understanding “this” in javascript…

Speak Your Mind

*