Mar 062012
 

Backbone in baby steps, Part 1

Topic: Backbone, Underscore, jQuery
Language: JavaScript
Difficulty: Medium
Pre requisites: Moderate JavaScript knowledge. Basic jQuery knowledge.

In this tutorial  we will be building a library application for managing digital books using Backbone. We will also have a glimpse at Underscore and jQuery since they are the default requirements of Backbone.

Backbone

Backbone is an MVC-style framework for building applications. It is not a true MVC framework, since it doesn’t have a controller, the controller responsibilities lies instead on the view. In addition to model and view, Backbone also has Collection for grouping models together, and router for handling url routes.

Setting up

First we need a simple folder structure for our project. Create the directories css, img and js in your project root folder. Download Backbone, Underscore and jQuery libraries and put in your js folder. Save this image in your img folder:

Create a new file index.html in the root of your project folder and enter this:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8"/>
    <title>Backbone.js Library</title>
    <link rel="stylesheet" href="css/screen.css">
</head>
<body>
<div id="books">
    <div class="bookContainer">
        <img src="img/placeholder.png"/>
        <ul>
            <li>Title</li>
            <li>Author</li>
            <li>Release date</li>
            <li>Keywords</li>
        </ul>
    </div>
</div>
<script src="js/jquery-1.7.1.js"></script>
<script src="js/underscore.js"></script>
<script src="js/backbone.js"></script>
<script src="js/app.js"></script>
</body>
</html>

So open this file in a browser and it should look something like this:

Not so awesome. This is not a css tutorial, but we need to do some formatting. Create a file screen.css in your css folder and type this:

body {
    background-color: #eee;
}

.bookContainer {
    border: 1px solid #aaa;
    width: 300px;
    height: 130px;
    background-color: #fff;
    float: left;
    margin: 5px;
}
.bookContainer img {
    float: left;
    margin: 10px;
}
.bookContainer ul {
    list-style-type: none;
}

Now it looks a bit better:

So this is what we want the final result to look like, but with more books. Go ahead and copy the bookContainer div a couple of times if you would like to see what it looks like. Now we are ready to start developing the actual application. Open up app.js and enter this:

(function ($) {

    var Book = Backbone.Model.extend({
        defaults:{
            coverImage:"img/placeholder.gif",
            title:"Some title",
            author:"John Doe",
            releaseDate:"2012",
            keywords:"JavaScript Programming"
        }
    });

})(jQuery);

This is our model of a book. It contains a defaults property that gives us default values for all the values we want our model to contain. Note that I have wrapped everything in a self executing function with the argument “jQuery”. This is standard procedure to prevent polluting the global scope and to avoid any collisions with the $ variable. The model in itself isn’t very useful so we need to pair i with a View. For the view we need a template so open up index.html and create a template from our bookContainer we created earlier:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8"/>
    <title>Backbone.js Library</title>
    <link rel="stylesheet" href="css/screen.css">
</head>
<body>
<div id="books">
    <div class="bookContainer">
        <img src="img/placeholder.png"/>
        <ul>
            <li>Title</li>
            <li>Author</li>
            <li>Release date</li>
            <li>Keywords</li>
        </ul>
    </div>
    <script id="bookTemplate" type="text/template">
        <img src="<%= coverImage %>"/>
        <ul>
            <li><%= title %></li>
            <li><%= author %></li>
            <li><%= releaseDate %></li>
            <li><%= keywords %></li>
        </ul>
    </script>
</div>
<script src="js/jquery-1.7.1.js"></script>
<script src="js/underscore.js"></script>
<script src="js/backbone.js"></script>
<script src="js/app.js"></script>
</body>
</html>

So what’s going on here? Well, I have wrapped the template in a script tag with the type “text/template”. By doing this the browser will not recognize the type and not render it, but we can still access all the elements in JavaScript. I have removed the surrounding div tag since the templating engine (Underscore) will create that for us. In the various fields I have inserted <%= field %> that Underscore will find and replace with real data. Now that we have our template in place we can go and create the view, so switch over to app.js:

(function ($) {

    var Book = Backbone.Model.extend({
        defaults:{
            coverImage:"img/placeholder.gif",
            title:"Some title",
            author:"John Doe",
            releaseDate:"2012",
            keywords:"JavaScript Programming"
        }
    });

    var BookView = Backbone.View.extend({
        tagName:"div",
        className:"bookContainer",
        template:$("#bookTemplate").html(),

        render:function () {
            var tmpl = _.template(this.template); //tmpl is a function that takes a JSON object and returns html

            this.$el.html(tmpl(this.model.toJSON())); //this.el is what we defined in tagName. use $el to get access to jQuery html() function
            return this;
        }
    });

})(jQuery);

So the view works like the model in that we use the extend function and pass it properties. The tagName property defines the container of the view, the className defines the class of the container and template is, well, the template for the view. The render function creates the tmpl function by calling underscores template function with one argument (the template from our index.html). It then calls the tmpl function with our model… wait a minute! What model? Well, this is something that we need to provide as an argument when we call out view constructor. As it looks now our view isn’t connected to any element in out html page either, so let’s take care of that now:

(function ($) {

    var Book = Backbone.Model.extend({
        defaults:{
            coverImage:"img/placeholder.gif",
            title:"Some title",
            author:"John Doe",
            releaseDate:"2012",
            keywords:"JavaScript Programming"
        }
    });

    var BookView = Backbone.View.extend({
        tagName:"div",
        className:"bookContainer",
        template:$("#bookTemplate").html(),

        render:function () {
            var tmpl = _.template(this.template); //tmpl is a function that takes a JSON object and returns html

            this.$el.html(tmpl(this.model.toJSON())); //this.el is what we defined in tagName. use $el to get access to jQuery html() function
            return this;
        }
    });

    var book = new Book({
        title:"Some title",
        author:"John Doe",
        releaseDate:"2012",
        keywords:"JavaScript Programming"
    });

    bookView = new BookView({
        model: book
    });

    $("#books").html(bookView.render().el);

})(jQuery);

Here we have created a new Book model by calling the Book constructor and passing an object with our desired properties. The model is then used when creating a BookView. Finally the bookView is rendered and inserted into our page. It should look something like this:

Looking good, we now have a working Backbone application. Since a library cannot contain just one book, we need to push further. Lets have a look at Collections. Collections contain collections of models. The models must be of the same kind, ie you cannot mix apples and bricks in the same collection. Other than that, collections are quite simple, you just tell them what kind of models they contain like this:

var Library = Backbone.Collection.extend({
    model:Book
});

Go ahead and insert the code in app.js. Next stop is a new view for our Library collection. This view is a bit complicated than the earlier BookView:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
var LibraryView = Backbone.View.extend({
        el:$("#books"),

        initialize:function(){
            this.collection = new Library(books);
            this.render();
        },

        render: function(){
            var that = this;
            _.each(this.collection.models, function(item){
                that.renderBook(item);
            }, this);
        },

        renderBook:function(item){
            var bookView = new BookView({
                model: item
            });
            this.$el.append(bookView.render().el);
        }
    });

I added some line numbers to help me explain what is going on. On line 02 I specified the property “el”. A view can take either a tagName, as we saw in our BookView, or an el. When we use tagName, the view will create the element for us, but we are responsible for inserting it into the page. When we use el we specify an existing element in the page and the view will write directly into the page, into the specified element. In this case we select the div with id=”books”.

Next on line 04 is the initialize property. This is a special (but optional) property that must contain a function. This function will be called by Backbone when the view constructor is called. On line 05 in we create a new Library (collection of Book models) and store it in a local property called “collection”. On line 06 it call its own render function, which means that as soon as we call the LibraryView constructor it will get rendered, so this is a self rendering view. We don’t have to make it self rendered but it is common practice.

On line 10 in the render function we store a reference to the current object in a variable “that” and then (on line 11) use the each function of underscore to iterate over all the models (Books) in our collection. The first argument to “each” is the array that will be iterated over. The second argument is the function that will be applied to each member of the array. The function in our case calls the renderBook function with the current model as argument. We need to use “that” to get this right since if we would have used “this” it would have referenced the function itself. The third argument (line 13) is the context that each is executing in.

On line 16 we define a function renderBook that takes a model (a Book) as argument and uses it to create a BookView. The bookView is then rendered and appended to the view container as specified in our el property (on line 02).

You may have noticed that on line 05 we called the Library constructor with the argument “books”. Library is a Backbone collection that expects an array of  objects that it can use to create Book models. We haven’t defined the books variable yet so lets go ahead and do that:

var books = [{title:"JS the good parts", author:"John Doe", releaseDate:"2012", keywords:"JavaScript Programming"},
        {title:"CS the better parts", author:"John Doe", releaseDate:"2012", keywords:"CoffeeScript Programming"},
        {title:"Scala for the impatient", author:"John Doe", releaseDate:"2012", keywords:"Scala Programming"},
        {title:"American Psyco", author:"Bret Easton Ellis", releaseDate:"2012", keywords:"Novel Splatter"},
        {title:"Eloquent JavaScript", author:"John Doe", releaseDate:"2012", keywords:"JavaScript Programming"}]

Now we are almost ready to try out our first version of the Backbone library. Replace this code:

    var book = new Book({
        title:"Some title",
        author:"John Doe",
        releaseDate:"2012",
        keywords:"JavaScript Programming"
    });

    bookView = new BookView({
        model: book
    });

    $("#books").html(bookView.render().el);

with this:

var libraryView = new LibraryView();

If you view index.html in a browser you should see something like this:

Here is the final app.js:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
(function ($) {
    var books = [{title:"JS the good parts", author:"John Doe", releaseDate:"2012", keywords:"JavaScript Programming"},
        {title:"CS the better parts", author:"John Doe", releaseDate:"2012", keywords:"CoffeeScript Programming"},
        {title:"Scala for the impatient", author:"John Doe", releaseDate:"2012", keywords:"Scala Programming"},
        {title:"American Psyco", author:"Bret Easton Ellis", releaseDate:"2012", keywords:"Novel Splatter"},
        {title:"Eloquent JavaScript", author:"John Doe", releaseDate:"2012", keywords:"JavaScript Programming"}];

    var Book = Backbone.Model.extend({
        defaults:{
            coverImage:"img/placeholder.gif",
            title:"Some title",
            author:"John Doe",
            releaseDate:"2012",
            keywords:"JavaScript Programming"
        }
    });

    var Library = Backbone.Collection.extend({
       model:Book
    });

    var BookView = Backbone.View.extend({
        tagName:"div",
        className:"bookContainer",
        template:$("#bookTemplate").html(),

        render:function () {
            var tmpl = _.template(this.template); //tmpl is a function that takes a JSON object and returns html

            this.$el.html(tmpl(this.model.toJSON())); //this.el is what we defined in tagName. use $el to get access to jQuery html() function
            return this;
        }
    });

 var LibraryView = Backbone.View.extend({
        el:$("#books"),

        initialize:function(){
            this.collection = new Library(books);
            this.render();
        },

        render: function(){
            var that = this;
            _.each(this.collection.models, function(item){
                that.renderBook(item);
            }, this);
        },

        renderBook:function(item){
            var bookView = new BookView({
                model: item
            });
            this.$el.append(bookView.render().el);
        }
    });

    var libraryView = new LibraryView();

})(jQuery);

So lets recap what happens here:

  1. Line 58: the LibraryView constructor is called
  2. Line 36: the LibraryView is tied to the books div in our html
  3. Line 39: the Library constructor is called with the array of book properties from line 02, creating a collection of Book models
  4. Line 43: The render function iterates over the Book collection and calls renderBook (line 46) for each Book model
  5. Line 51: renderBook function creates a new BookView from the given Book model
  6. Line 54: The render function of BookView is called and the result is appended to the books div in out html

You have now been introduced to the basic components of Backbone Model, View and Collection. In the next part I will continue to develop our Library into something more interactive.

The code for this tutorial is available on github.

  30 Responses to “Backbone in baby steps”

  1. This is a great tutorial, very easy to follow, please continue it!

    thanks.

  2. you can rewrite lines 45-47:
    _.each(this.collection.models, function(item){
    that.renderBook(item);
    }, this);

    with this little peace of code:

    this.collection.each(this.renderBook, this)

    • Great observation. So I saved the scope of the render function in a variable ‘that’ to be able to access it in the ‘each’ function parameter, but since the third parameter to ‘each’ is the scope that the supplied function will execute in, the whole ‘that’ thing is not necessary. Also applying ‘each’ directly to the collection is very neat. Thanks!

  3. I had to remove the original div with class=bookContainer as it created a blank div before the other data filled ones, unless I missed something.

  4. If you put the scripts between the head tags, you’ll get a JavaScript error in underscore.js. I didn’t even this was a potential problem, and I am not sure why it was, but it is and I only noticed it when I couldn’t get the code to work using some pre-made templates I had for testing new code; the code worked perfectly when I left it as in the code… and now I am wondering if it is because of the #bookTemplate script. I’ll have to play with this, but it was interesting enough of issue for me while learning that I thought I’d share. :)

  5. I read this article fully about the comparison of latest and preceding technologies, it’s awesome article.

  6. You really make it seem so easy along with your presentation but I in finding this topic to be actually something which I feel I’d by no means understand. It sort of feels too complicated and very broad for me. I am having a look forward on your subsequent post, I’ll try to get the dangle of it!

  7. How does it come that this tutorial looks just like Addy Osmani’s Erarly Book-release on github?!!!!!! Check it your self code in screendumps are the same; http://addyosmani.github.com/backbone-fundamentals/#TOC

  8. Aha, okay…. It’s that good he wanted to use it. Frankly and excuse me I thought you were one if those copycats in the interwebs. Now I can sleep safe ;-)

  9. Thanks for sharing your knowledge. Backbone excellent tutorial! :D

  10. Excellent tutorial. I’ve purchased a video course, read lots of different articles on Backbone and know quite a bit, but I was confused about how all the different components of Backbone all go together – I had a patchwork of elements. This is a very good example that sews together Model. View and Collection. Now I “get it” and I can re-read other stuff and understand the wider context.
    Thank you very much! This is a brilliant way to introduce Backbone, and should be linked to from http://www.backbonejs.org!

  11. Wow, thank you so much for creating this. This tutorial was exactly what I needed to get up to speed on this material.

  12. Just a tip: it might be worth inter-linking the three parts of this tutorial for easy access.

  13. Excellent intro-level tutorial, thanks!

  14. Please if I need to call books from database for example how I can make this please

  15. Björn Ekengren thank you!
    I need you advice…
    Will app be work local without server or I should run server?

  16. Great tutorial. One correction: img/placeholder.gif should be img/placeholder.png and would be good to remind those of us who are a bit slow (!) to update the script source urls to e.g. js/jquery-1.10.2.min.js. Thanks.

  17. I found that tutorial very helpful and easy .Thanks

  18. I really want to book mark this specific posting, “Backbone in baby steps | CodeByExample” on my
    website. Do you care if I personallydo? Thx -Kerstin

  19. Found helpful.

    But this seems like hard-coded. Can we create a MySQL DB to manipulate app.js in-sync with DB?
    Eager to hear from your side.

    Thanks.

    • Hello Kushal,
      I’m not sure what you mean here, but using Mysql instead of Mongo should be fine. You probably need to take care of serialisation/deserialisation since Mysql is a relational database. The part of keeping the app in sync with the database might be a bit more complicated since the server needs some way to tell when the data changes and then some way to tell the web app that data is changed. Should be doable with database hooks and websockets but all this is probably more suitable for another blog post.

  20. Great Tutorial..Can u also please explain about the router concept..Thanks in advance

  21. Great tutorial, just what I was looking for. I look forward to the next steps.

  22. Great tutorial Man!
    :-)
    //San

  23. I like it keep it up.

  24. Great tutorial.one of the simplest.

  25. Great tutorial.

 Leave a Reply

(required)

(required)

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>