I’m by no means an expert in Backbone.js. I created this post because I had difficulty understanding backbone, and I hope that the information that I provide will help others grasp Backbone.js a little faster. This post is directed to individuals who understand the concepts of Backbone.js, but are having a difficult time implementing a simple solution.

This post provides a simple example on Backbone.js Routers, Models and Collections and Views. I’m going to try to make this example extremely simple. But also show key Backbone.js concepts

I’m probably not the best writer. It would take me days, if I were to validate all the grammar, spelling and such. So I’m just going to try to get it done. Even though the grammar and such may not be perfect, the technical parts of the article should be correct. I hope you enjoy.

A few months after writting this post, my priorites and technology direction changed. For probably 6 months I did little work with Backbone.js. When I came back to Backbone.js in November, I was a little lost. I had to become familar with the technology once again. I thought it would be a good opportunity to write about Backbone.js basics. Here are posts where I describe the basics of Backbone.js.

This post will be based on a contrived idea of displaying a list of movies at a theater.



The complete solution can be downloaded from here:

Source – Backbone Theater Zip

Here’s the structure of my website.



In the image above, there are 3 files (Backbone, jQuery, and Underscore) in the lib directory. Backbone has a hard dependency on underscore and must be referenced, but a soft dependency on jQuery.

This example will not access a server, so to simulate the retrieval of data from the server I have a file name movies.json in the data directory. Here’s an example of the file:

[ { "Id": "BVwi1", "Name": "Bag It", "AverageRating": 4.6, "ReleaseYear": 2010, "Url": "http://www.netflix.com/Movie/Bag_It/70153545", "Rating": "NR" }, { "Id": "BW1Ss", "Name": "Lost Boy: The Next Chapter", "AverageRating": 4.6, "ReleaseYear": 2009, "Url": "http://www.netflix.com/Movie/Lost_Boy_The_Next_Chapter/70171826", "Rating": "NR" }, ….]

Theater.htm will be the file the user navigate to in the browser. This file is fairly simple and we will look at it in more detail shortly.

Last but not least in the script directory is the main.js file. This file is where all the magic happens.

To get started, we will look at the theater.htm file

My Theater

All theater.htm file does is reference scripts. We are currently referencing jQuery, Underscore, and Backbone. Since there are dependencies for the scripts, it’s important that these files are referenced in this order.

The last script referenced is main.js; lets create it.

Create a new file call main.js in the scripts directory

It’s import to namespace our application so that there is a less possibility of name collision. To start out add the following code.

var Theater = { Models: {}, Collections: {}, Views: {}, Templates:{} }

This creates a Theater object that includes Models, Colllections and Views object. This is a standard way of organizing a Backbone application, but not the only way. I also added a template object. This is where we will store our html templates; this will be used later.

Since we will be working with movie data, we will create a movie model

Theater.Models.Movie = Backbone.Model.extend({}

This looks fairly simple, but trust me, this is all we need. We are creating a model class that extends the Backbone.Model. We could add code within the {}, but for now we don’t need to.

We will be working with a collection of movies, so below we will create a collection class called Movies.

Theater.Collections.Movies = Backbone.Collection.extend({ model: Theater.Models.Movie, url: "scripts/data/movies.json", initialize: function(){ console.log("Movies initialize") } });

This class is created by extending Backbone.Collection. In the code we identify that the model will be based on the Movie model we created earlier. Based on my experience with Backbone, initialize will be call anytime a new Backbone object is created.

The url identifies where data should be retrieved from. Usually this would access the server, but for our simple example we will be access a file that includes the json data.

When the Movies object get created (see below) the initialize function will execute automatically by backbone. We could do this without routes, but I believe this show a more complete example.

When Theater.htm loads main.js, something needs to start the application and this is where routes are used.

Theater.Router = Backbone.Router.extend({ routes: { "": "defaultRoute" //http://localhost:22257/Theater/theater.htm }, defaultRoute: function () { console.log("defaultRoute"); var movies = new Theater.Collections.Movies() movies.fetch(); console.log(movies.length) } }) var appRouter = new Theater.Router(); Backbone.history.start();

When a user navigates to the Theater.htm page, the Router will identify where the application will start. In this example there is a route that identifies what method (defaultRoute) to call when the page loads.

In the defaultRoute method we call console.log so that we can confirm that the route is working correctly.

Next, we create a new Movies object. And we call movies.fetch(). Based on the url that was identified in Theater.Collections.Movies, when movies.fetch is call a request to movies.json will be executed. Fetch does an asynchronous call. So when we call console.log(movies.length) the value displayed should be 0.

To start the application, a Router object is created and Backbone.history.start is executed. When start is call the appRouter will be executed.

No if we navigate to the page everything should work.

I’m testing my application in Chrome. Below is an image of the results of console.log and how the files are loaded.

In the previous image we can see that the defaultRoute did get executed. The Movies object did get created, and since the fetch is asynchronous the results were not available and 0 was displayed.

In the Network image above we can see the JavaScript files load, and as expected the movies.json file didn’t load until the fetch was called.

In the development tools for Chrome, we can see that the application is working, but nothing is displayed to the user.

I believe there are some important concepts to understand and will not display movies unit the very end.

Lets turn our attention back to the main.js file.

Add the following code after the location Theater.Collections.Movies” is created but before routes. The following view is extremely over simplified. Hopefully this will help clarify how a view works. I promise, we will display the movies.

Theater.Views.Movies = Backbone.View.extend({ initialize: function () { _.bindAll(this, "render"); this.collection.bind("reset", this.render); }, render: function(){ console.log("render") console.log(this.collection.length); } })

When the Theater.Views.Movies (describe below) is create, the first method called is initialize. Initialize is used to setup the View.

Since the view’s methods, such as render, will be execute out of context, we need to make sure that “this” always reference the current view context. In the render function we reference “this”. If we did not include “_.bindAll(this, “render”);” in initialize then “this” in the render function would not be valid.

Next is probably one of the more important lines, and in the context could be one of the more confusing lines up to this point the object “this.collections” has not been seen. When looking at this code, “this.collections” has not been defined. Latter we will create a Theater.Views.Movie object. In the future when we create this view, a collection will be passed to the constructor (see following code). This will create the view and set the collection to the movies collection.

new Mhs.Views.Movies({collection: movies});

This seem like magic to me and is confusing, but it works. To help make this more understandable, you could add the following line before initialize, but it’s not needed.

collection: new Theater.Collections.Movies(),

Sorry for the sidetrack, hopefully that clarified some things.

Now back to “this.collection.bind(“reset”, this.render);” and what it does. This procedure identifies that whenever the collection entire contents is change then execute this.Render. In the code above, in Route we call movies.fetch(); fetch returns all the values from the data store. When fetch completes and the collection is updated then this.render is executed.

Now to the render function. Because of the bind we created earlier, anytime the collection is reloaded the render function will be executed. In this function we are not doing much, but we will. We are writing to the log that we made it to the render function. We are also writing to the console the length of the collection. If we did not include “_.bindAll(this, “render”);” in the initialize then the collection.length would not be valid.

We could refresh the browser, but since the view has not been created, nothing should be different. But this would be a good time to confirm there are not bugs.

Now that we have the view defined, we now need to create the view. Add the following line to the defaultRoute function that we created earlier.

var movies = new Theater.Collections.Movies() new Theater.Views.Movies({ collection: movies }); //!!!! Add this line movies.fetch();

The first and third line should already be included the defaultRoute function. The first line creates the movies collection. The second line creates a Movies View and passes the movies collection, which is empty, to the Movies View. This collection will be assigned to the collection in the view. Remember that in view initialize function that when the entire collection changes, call the view’s render function. At this point we have not called fetch on the collection, so the view’s render function has not been execute; only the view’s initialize has be ran.

Next we execute the collections fetch by call movies.fetch(). When the fetch completes and the values from the fetch have been assigned to the collection, then the collections event called “reset” will be fired and in-turn the view’s render function will be executed.

This is how views reacts to changes in collections.

So now if you refresh your browser, you will see that the render gets called and that there are items in the collection. There’s no HTML yet, but we can see things are working.

We are still not to the point of displaying the movies. I want to show one other thing. I want to show what happens when we add an additional item to the collection and how the view is notified about the new item.

We need to change some code in defaultRoute function. In the defaultRoute function, any place where movies (lowercase) is reference change it to Theater.movies. We need to be able to get a reference to the movie collection from outside of the Theater namespace. The default Route should be identical to the code below. If you refresh your browser, everything should work the same.

defaultRoute: function () { console.log("defaultRoute"); Theater.movies = new Theater.Collections.Movies() new Theater.Views.Movies({ collection: Theater.movies }); Theater.movies.fetch(); console.log(Theater.movies.length) }

In your html file, add the last line in the following code segment to the body below

My Theater

Now in you main.js file at the very bottom add the following code

//This is a hack for demonstration purposes $("#butAddItem").click(null, function () { var movie = new Theater.Models.Movie( { "Id": "BVP3s", "Name": "Lord of the Rings ", "AverageRating": 4.3, "ReleaseYear": 2003, "Url": "http://www.netflix.com/.....", "Rating": "PG-13" } ) Theater.movies.add(movie); console.log(Theater.movies.length) })

So when the “Add Item” button is click create a new movie and add the movie the Theater.movies collection. Also display the length of the collections. When you run this, the only thing that should happen is that the length of the collection should change increment by 1.

We need to tell the view that when an item is added to the collection, the view should be updated. If the view makes the following changes, add the string “addOne” to the _.bindAll in the initializer:

_.bindAll(this, "render", "addOne")

Add the following line to the initializer:

addOne: function (model) { console.log("addOne") }

So your view should look like this

Theater.Views.Movies = Backbone.View.extend({ initialize: function () { _.bindAll(this, "render", "addOne"); this.collection.bind("reset", this.render); this.collection.bind("add", this.addOne); }, render: function(){ console.log("render") console.log(this.collection.length); }, addOne: function (model) { console.log("addOne") } })

If you now refresh your browser and click the “Add Item” button, you will see that the view is notified about the new item that was added to the collection.

Now it’s time to display the movies.

First we will edit the HTML file and add the templates and a container. Add following code after the button

The mainContainer div is where the movies will be displayed. Since scripts are of type “text/template”, the browser will ignore everything in these script tags. The type of “text/template” doesn’t mean anything to the browser, it’s there for developer. We could have made the type “cool/tmplt”. It doesn’t matter much what the type is.

Each script does have an id. Code in the main.js we will use this id to find these templates.

The template “tmplt-Movies” is very basic, this template could include much more, but for simplicity reasons we will just include the ul div. Each movie will be included in the ul div

For “tmplt-Movie” script, each movie in the collection will execute against this template and the results will be added to “tmplt-movie” ul div. The is the syntax for underscore template engine. Each variable in the template will have a matching property for each movie model. This template is fairly simple as it should be, but depending on needs templates could be much more complex.

The code below can replace the view that you currently have. I will go over this code below.

Theater.Templates.movies = _.template($("#tmplt-Movies").html()) Theater.Views.Movies = Backbone.View.extend({ el: $("#mainContainer"), template: Theater.Templates.movies, //collection: new Theater.Collections.Movies(), //Not needed initialize: function () { _.bindAll(this, "render", "addOne", "addAll"); this.collection.bind("reset", this.render); this.collection.bind("add", this.addOne); }, render: function () { console.log("render") console.log(this.collection.length); $(this.el).html(this.template()); this.addAll(); }, addAll: function () { console.log("addAll") this.collection.each(this.addOne); }, addOne: function (model) { console.log("addOne") view = new Theater.Views.Movie({ model: model }); $("ul", this.el).append(view.render()); } }) Theater.Templates.movie = _.template($("#tmplt-Movie").html()) Theater.Views.Movie = Backbone.View.extend({ tagName: "li", template: Theater.Templates.movie, initialize: function () { _.bindAll(this, 'render'); }, render: function () { //return this.template(this.model.toJSON()); //Correction return $(this.el).append(this.template(this.model.toJSON())) ; } })

The first thing is to find the template in the DOM for movies by searching for ”tmplt-Movies” and assign it to an object. This also compiles the template, so we don’t need to compile it again.

Theater.Templates.movies = _.template($("#tmplt-Movies").html())

In Theater.Templates.movies add the following two lines before the initialize function.

el: $("#mainContainer"), template: Theater.Templates.movies,

Here we assign the mainContainer div that is in the html file to el; this is where all the movies will be written to. el is a special identifier that Backbone uses. If el is not included then Backbone assign

to el. Template is used as a shortcut. This seems standard in most examples I’ve seen. In the initialize function change the “_.bindAll” to include “addAll” _.bindAll(this, "render", "addOne", "addAll"); Remember the _.bindAll provides the correct context of “this” in each of the functions listed In render function add the following two lines after the console.log $(this.el).html(this.template()); this.addAll(); The first line gets the template and inserts it into the mainContainer which is represented by el Add the following addAll function after the render function addAll: function () { console.log("addAll") this.collection.each(this.addOne); }, The this.collection.each iterates over each item in the collection and calls the addOne function in the view. In the addOne function add the following two lines after the console.log view = new Theater.Views.Movie({ model: model }); $("ul", this.el).append(view.render()); The first line creates a new Movie View, which has not been defined yet. When creating this view a movie model from the collection is passed to the constructor of the Movie View. The render function of the movie view is executed and the results are added to the ul div in the mainContainer. Last but not least, create the Movie View. Add the following code after the Theater.Views.Movies and before Theater.Router Theater.Templates.movie = _.template($("#tmplt-Movie").html()) Theater.Views.Movie = Backbone.View.extend({ template: Theater.Templates.movie, initialize: function () { _.bindAll(this, 'render'); }, render: function () { //Correction return $(this.el).append(this.template(this.model.toJSON())) ; } }) We did this for the previous view; the first thing is to find the template in the DOM for “tmplt-Movie” and assign it to an object. This also compiles the template, so we don’t need to compile it again. In the definition of Theater.Views.Movie create shortcut to the template In initialize, set the context of “this” for the render function. The render function gets the json of model, pass the json to the template and return the results of the template. The results will be used by the parent view to be displayed Now if you refresh the browser this is what you should see. Now all the movies have been display in the browser Here’s what the result html looks like In the html file, if you move the “Add Item” button below the container you can see how new items added to the collection are displayed in the view. I hope this is helpful to others. Hopefully in a few days, I will get the code post in its entirety. Reference: Movies from NetFlix ODATA

Backbone.js

Backbone Fundamentals – Addy Osmani

Backbone Tutorial

Backbone.js Live Collections