Overlay a Simple BackboneJS Application

This is Part Two of the Backbone Reactive Tutorial. For all sections, please view the Table of Contents.

Now the static fallback content has been created and is working, we start overlaying a basic Backbone application over the top.

We will leave the homepage for the moment, coming back to that later on in this project. For now we will concentrate on the List page. The functionality of the List page is very simple: all it does is replace the static content on the List page with some dynamically generated content from JSON data retrieved by an Ajax call.

Note: You can find all the source code to this project on GitHub, this chapter has been tagged ch2-simple.

Dependencies

In the list.html page we simply need to load in the required Javascript files for our application. Obviously there is Backbone, but we also need to include its dependencies, underscore and either jQuery or Zepto. In our case we will use jQuery for the time being, as it is the framework most people are familiar with. (As we progress with developing for mobile devices we will be investigating Zepto as a replacement).

<script type="text/javascript" src="/js/jquery-1.8.3.min.js"></script><script type="text/javascript" src="/js/underscore-min.js"></script><script type="text/javascript" src="/js/backbone-min.js"></script>

Once we’ve loaded in our dependencies, we need to load in our actual application file:

<script type="text/javascript" src="/js/app/list.js"></script>

It is this file, list.js that does all the work. There is quite a lot going on in this file, even for a simple app such as this, so let’s brake it down piece by piece.

JSLint

It is a good idea to lint your Javascript code. The main reasons for this is to give all your code a consistency so it is easier to come back to later. It also catches any silly typos or bugs along the way.

For linting this project, I am using the SLint NodeJS module, the lint command used is:

jslint -indent=2 --plusplus --vars ./js/app/list.js

JSLint can be a bit bossy, so you’ll notice that the first few lines in the code are simply there to keep there to keep JSLint happy.

  'use strict';
  var Backbone; // hello jslint
  var $; // hello jslint

Now we have the tedium of keeping code tidy out the way, we can move onto the actual meat and potatoes.

Models

Reading through the rest of the list.js file, you’ll see that it follows a pattern of building up each needed component one at a time.

Firstly a ListItem Model is defined. This very simply extends the base Backbone Model.

var ListItem = Backbone.Model.extend({});

This line is so simple because Backbone Models are so powerful. But why do we need a model in the first place?

The way I think about it, models are the basic units of data within a Backbone application. They are the smallest piece of complete information within context. For example, in our project, the smallest piece of complete information is an item of the list. This item itself has smaller pieces of data, such as a date or subject, but without the wider context of the item itself, these tiny pieces of data make no sense – they are not complete.

Models don’t just contain data, they also have logic handling that data: validation, access control etc as well as events bound to that data. For example, if the UI changes a property of the model, this can trigger an event so other parts of the UI can react. This is why they are so powerful.

However, for our purposes we are going to explore none of that right now. Instead we’ll move onto Collections

Collections

Whilst models provide the individual bits of data, Backbone Collections, as their name suggests, collate these individual bits of data into one group, or collection. The important thing to remember about collection are that the models within them are ordered. This allows us, for example, to load in ordered data and have this order honoured within the collection.

When defining a collection we want to start using dynamic data. Backbone has built in functionality for this, which greatly reduces the amount of code needed to be written for ajax calls etc.

To start using dynamic data in a collection we must specify a URL. This URL is for the ajax request when we call the collection’s fetch() method. (For the purposes of this tutorial we are simply using mock JSON data, not a dynamic backend). As we want the data to load in straight away we call the fetch() method in the initialize function, passing it any callback options.

Backbone has another built in method – parse() – to interpret the data received within the response from the ajax request. In our case, we simply want to return exactly the data that we fetched from the mock JSON file.

var ListCollection = Backbone.Collection.extend({
model: ListItem,
 
initialize: function (models, options) {
this.fetch(options);
},
 
// As we don't really care for the backend in this demo, we have
// hard-coded some JSON data to use in the application
url: '/mock-data/list.getList.json',
 
parse: function (response) {
return response;
}
});

As with the Models, Collections are simple, but powerful.

For reference, the format of the JSON data returned is as follows:

[
{"id": 5, "date": "1357643063000", "subject": "Testing the Backbone Reactive List Page", "from": "Rob" },
{"id": 4, "date": "1357642823000", "subject": "Another exciting subject line", "from": "John" },
{"id": 3, "date": "1357641983000", "subject": "I just love dancing", "from": "Billy" },
{"id": 2, "date": "1357565783000", "subject": "Possible expansion plans", "from": "Wilhelm" },
{"id": 1, "date": "1357563263000", "subject": "Can't think of anything witty", "from": "Susan" }
]

Views

Backbone Views are where the action really gets going.

Now we have defined our data and how it is organised, we need to start using that data. What this means for us is that we need to start displaying this data within the browser.

The way I like to think about views is that they represent individual components or groups of components within the user interface. In our current list page we have broken it down into two views:
- a view for the complete list, in our UI this is a table
- a view for the individual list elements, or table rows in our case

This relates very closely to Collections (for the complete list) and Models (for the individual rows). In fact, Backbone allow us to attach both models and collections directly to views during initialization. something we will look at in a moment.

First we define the view for the table row, or list item:

var ListRowView = Backbone.View.extend({
tagName: "tr",
className: "list-row",
 
....
});

Setting the tagName property to tr means that when inserted into the DOM, this view will be a table row element. We also add a class name, thinking about it I’m not sure why, possibly just because we can.

Right now, this view is a little useless, so we need to implement the render() method:

render: function () {
this.id = this.model.get("id");
 
// I am not spending too much time on the date formatting or
// making it look nice, I just need enough so it works
var date = new Date(parseInt(this.model.get('date'), 10));
var timeNice = this.padTime(date.getHours()) + ':' + this.padTime(date.getMinutes());
var dateNice = date.toLocaleDateString();
 
var subject = this.model.get("subject");
var from = this.model.get("from");
 
// Later I'll be converting this to a template, but for the time
// being it will suffice
var html = '' + timeNice + '' + dateNice + '
 
' +
'' + subject + '' + from + '
 
';
 
this.$el.append(html);
return this;
},

The first thing to notice is that we are associating the ID of this view instance with that of the view’s model. This is important when it comes to triggering and responding to events later.

However, we have neatly glossed over where the model comes from. How does the View contain a model from which it can use it’s ID?

As mentioned above, views can have collections and models attached to them during initialization. Jumping ahead of ourselves slightly, in the ListView view, we can see that when we loop through the collection of items, we initialize the ListRowView passing it a ListItem model instance.

self.collection.each(function (listItem) {
var v = new ListRowView({model: listItem});
...
});

The rest of the render() method is pretty self explanatory (if not incredibly quick and dirty, we will fix this in a later post).

We first clean up some of the model’s properties so they are suitable to be displayed to the general public. Next we build the HTML for the table row and then append this HTML to the table row element the view represents. Finally, we return ourselves as is convention.

So that is the individual table rows sorted. Next we need to look at the complete list as a whole.

As ever, we simply extend the base Backbone View:

var ListView = Backbone.View.extend({
 
});

However, unlike the ListRowView we are going to define an initialize method:

initialize: function () {
var self = this;
self.collection = new ListCollection({}, {
// the success function is used for the fetch callback
success: function () {
self.render();
}
});
},

This is doing three very important things so let’s pay close attention to them.

Firstly it is initializing the ListCollection. As described above, the act of initializing this collection will fetch the data from the mock JSON we have and assigning the results to itself.

Secondly we are assigning this ListCollection to be the current collection for this view. This will allow us to access the Collection data and contained Models far easier later on.

Finally, once the data has been loaded and the view’s collection populated we render the view.

render: function () {
var self = this;
var html = ' [ edited for brevity ] ';
self.$el.html(html);
 
self.collection.each(function (listItem) {
var v = new ListRowView({model: listItem});
v.render();
self.$el.children("table").append(v.el);
});
 
return self;
},

As we have already dealt with the most important part of this render method above, the rest of it should be pretty self-explanatory. However, to quickly summarize we are first creating a bunch of HTML represented by this view (yes yes, I know! We will move it to a template in a later post) and then injecting it into the DOM. Next we loop through each model in the collection, initializing a new view for each model and then appending that to the table element we have just injected into the DOM. Finally, we return the view itself as is convention.

Running the App

Finally, now all the Models, Collections and Views have been defined, we can run the app. This is simply a case of initiating the ListView, applying it to a specific DOM element. In our case it is #content.

That is it. To test it out, fire up the _server.js NodeJS HTTP server and navigate to http://localhost:7777/list.html. You should see a table of list items, which has been entirely populated by your Backbone application.

Congratulations. The first step is complete. On to events.

Reminder: you can find all the source code to this project on GitHub, this chapter has been tagged ch2-simple.