Learning Backbone.js Chapter 1: A Quick Overview

I've found it difficult to find a guide for learning Backbone.js. They're probably out there -- but it isn't easy. This is the first in a series of articles dedicated to giving a complete overview of Backbone.js through simple examples and concise code.

Something of a cookbook, you might say.


What you'll need:

  1. Underscore.js: A required library for Backbone, it also adds over 60 functions for making Javascript development easier.
  2. Backbone.js: The meat of it. This is what we will use for all the magic.

What we're making

Today we'll make a map which displays tweets via the Google Maps API and twitter search results (For the sake of simplicity, we won't go in to the Twitter API yet). We'll focus on fundamental construction using the Model, Collection ** and **View components of Backbone.

The twitter map shows tweets around the world.

Hopefully, by the end of this you will have a beautiful night sky lit up by tweets around North America. Let's get started!


A brief introduction:

Backbone.js is used to "supply structure to Javascript-heavy applications"1. It separates logic into Models, Views, Collections, and Routers.

The architecture of Backbone.js

A typical Backbone application will use models to keep track of information, assisted by collections (groups of data). Views help display this information, by binding themselves to changes in data. Routers help orchestrate all of this by providing the ability to navigate to various parts of an application seamlessly.

Backbone is Model Driven

At the core of all Backbone applications are models. Models, as you may have guessed, act as the base level of information. Since the purpose of our application is to display tweets, let's create a model to define them here:

1 // Creates a "Tweet" model based upon the Backbone.Model object
2 var Tweet = Backbone.Model;
3  
4 // Instantiates the tweet model:
5 var myTweet = new Tweet({
6   from_user : "natehunzaker",
7   text : "Grow you a #backbone for great good!"
8 });

What just happened? First we created duplicate of the Backbone Model object. Next we instantiated it as a new Tweet within the myTweet variable.

Pretty simple. But let's say we don't want a carbon copy of the Backbone model? Perhaps we need to add custom methods to our model, or we want to define the same defaults for every instance of the Tweet model. Let's create a new model which extends the base Backbone.Model object:

 1 // Creates a "Tweet" model based upon the Backbone.Model object
 2 var Tweet = Backbone.Model.extend({
 3     defaults: {
 4         from_user : "natehunzaker",
 5         text      : "Grow you a #backbone for great good!"
 6     }
 7 });
 8 
 9 // Instantiates the tweet model:
10 var myTweet = new Tweet();
11 
12 console.log(myTweet.toJSON()); //= { from_user: "natehunzaker", text: "Grow you a #backbone for great good!"

I threw in another function here, .toJSON(). For models, this will return a JSON representation of the model's attributes. When used on collections, it will return an Array of JSON objects for each model it contains.

Working with Collections.

The Tweet model will work well for this application, however we'll need to keep track of more than one of them. This is where collections come in to play. Collections are designed to create, delete, and manage groups of models. Let's create one here:

 1 var Tweet = Backbone.Model;
 2 
 3 var tweetsCollection = new Backbone.Collection({
 4     model: Tweet
 5 });
 6 
 7 tweetsCollection.add({
 8     from_user : "natehunzaker",
 9     text      : "Grow your JS a #backbone for great good!"
10 });

Noticing a trend? Here we first create a Tweet model. Then we create the TweetsCollection collection which references this model. Finally, we use the add method of collections to create a new Tweet within with the collection.

Backbone objects have events

In addition to this, all backbone objects can respond to events. Say, for example, you wish to perform an action every time a new record is added to a collection. Consider the following:

 1 var Tweet = Backbone.Model;
 2 
 3 var tweetsCollection = new Backbone.Collection({
 4     model: Tweet,
 5     
 6     initialize: function() {
 7         
 8         this.bind('add', function(model) {
 9             alert("Oh snap, we have a model!");
10         });
11 
12     }
13 
14 });
15 
16 
17 // Will fire the 'add' event of the TweetsCollection
18 tweetsCollection.add({
19     from_user : "natehunzaker",
20     text      : "Grow your JS a #backbone for great good!"
21 });

Pretty simple huh? Backbone events are powerful, I would recommend doing some reading on what events are available to models and collections. There are some really neat things you can do with them.


Pulling it all Together

Now that we've established some form of a basic understanding, let's get in to the application! First we need some initial construction:

 1 <!doctype html>
 2 <html>
 3   <head>
 4     <meta charset="utf-8">
 5 
 6     <title>Twitter Map</title>
 7     
 8     <link rel="stylesheet" href="css/style.css" />
 9     
10     <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.3/jquery.min.js"></script>
11     <script src="https://maps.googleapis.com/maps/api/js?sensor=true"></script>
12     <script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.3.3/underscore-min.js"></script>
13 
14   </head>
15   
16   <body>
17     
18     <header>Twitter Map</header>
19     
20     <div id="map_canvas"></div>
21     
22     <footer></footer>
23     
24     <script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.2/backbone-min.js"></script>
25     <script src="js/map.js"></script>
26     
27   </body>
28 </html>
 1 /* http://meyerweb.com/eric/tools/css/reset/ 
 2    v2.0 | 20110126
 3    License: none (public domain)
 4 */
 5 
 6 html, body, div, span, applet, object, iframe,
 7 h1, h2, h3, h4, h5, h6, p, blockquote, pre,
 8 a, abbr, acronym, address, big, cite, code,
 9 del, dfn, em, img, ins, kbd, q, s, samp,
10 small, strike, strong, sub, sup, tt, var,
11 b, u, i, center,
12 dl, dt, dd, ol, ul, li,
13 fieldset, form, label, legend,
14 table, caption, tbody, tfoot, thead, tr, th, td,
15 article, aside, canvas, details, embed, 
16 figure, figcaption, footer, header, hgroup, 
17 menu, nav, output, ruby, section, summary,
18 time, mark, audio, video {
19  margin: 0;
20  padding: 0;
21  border: 0;
22  font-size: 100%;
23  font: inherit;
24  vertical-align: baseline;
25 }
26 /* HTML5 display-role reset for older browsers */
27 article, aside, details, figcaption, figure, 
28 footer, header, hgroup, menu, nav, section {
29  display: block;
30 }
31 body {
32  line-height: 1;
33 }
34 ol, ul {
35  list-style: none;
36 }
37 blockquote, q {
38  quotes: none;
39 }
40 blockquote:before, blockquote:after,
41 q:before, q:after {
42  content: '';
43  content: none;
44 }
45 table {
46  border-collapse: collapse;
47  border-spacing: 0;
48 }
 1 @import url("reset.css");
 2 
 3 @import url(http://fonts.googleapis.com/css?family=Lobster);
 4 
 5 body {
 6     font-family: sans-serif;
 7 }
 8 
 9 header {
10     background: #222;
11     box-shadow: inset 0 -3px 5px #000, 0 1px 0 rgba(255,255,255, 0.15);
12     color: #fff;
13     font: 400 16px/2.5em "Lobster", sans-serif;
14     text-shadow: 0 1px 3px #000;
15     text-indent: 15px;
16 }
17 
18 #map_canvas {
19     width: 100%;
20 }

Cool. Now in your js folder, add a file called "map.js". We'll add all of the backbone code into here. From this point on we'll pick up quite a bit of speed, but I'll be sure to comment on everything new along the way.

Let's recycle some of the code made earlier, with some additions:

 1 // Twitter Map
 2 
 3 $(function() {
 4 
 5 //-- Data ---------------------------------------------------------------------//    
 6     
 7     var Tweet = Backbone.Model;
 8     
 9     var Tweets = Backbone.Collection.extend({
10         
11         // The search url which will pull in all tweets 1000 miles from roughly the center of the United States
12         url: "http://search.twitter.com/search.json?callback=?&count=100&rpp=100&geocode=35.5,-100,1000mi",
13 
14         // Filters information before it is passed into the collection
15         parse: function(response) {
16             
17             // Filter all tweets without a specific geolocation
18             var filtered = _.filter(response.results, function(d) {
19                 if (d.geo !== null) {
20                     return true;
21                 }
22             });
23             
24             this.add(filtered);
25             
26         },
27             
28         initialize: function() {
29           
30           // Search for more tweets every 2 seconds
31           setInterval(function() {
32               console.log("Fetching fresh data...");
33               tweets.fetch();
34           }, 2000)
35           
36           this.fetch();
37         }
38   });
39 
40 });

Overall not too bad, but there are some new elements here. url, fetch, parse, and initialize.

  • url: Backbone is designed enable easy access to information on a server. When we set the url property, we tell the collection where it can find information.
  • fetch: Once a url has been established, fetch can be used to retrieve new information. In this case, we will request a json object from the Twitter search service.
  • parse: A handy function. Whenever the fetch event is fired, parse enables you to preprocess data before it is stored in the collection. In example above, we use the underscore method _.filter() 2 to prevent any tweets that do not have a specific geolocation from being stored.
  • initialize: A staple of Backbone. Initialize is a method of each backbone object which will always run upon instantiation (in this case, the collection). Here we tell it to fetch new information from the server at an interval of 2 seconds.

Using Views

In order to display these tweets, we'll need to create a view for the map. As said earlier, views are the presentational element associated with Backbone; we'll use them to display content from our collection. I think views are best taught through example, so let's jump right in to it. Take a close look at the preceding code and add it right after your model and collection code

 1 $(function() {
 2 
 3 //... Model and Collection code ...//
 4 
 5 
 6 //-- Views --------------------------------------------------------------------//
 7     
 8     var Map = Backbone.View.extend({
 9         
10         el: $('#map_canvas'),
11                         
12         initialize: function() {
13             
14             // Roughly the center of the United States
15             var latlng = new google.maps.LatLng(35.5, -100);
16             
17             // Google Maps Options
18             var myOptions = {
19                 zoom: 5,
20                 center: latlng,
21                 mapTypeControl: false,
22                 navigationControlOptions: {
23                 style: google.maps.NavigationControlStyle.ANDROID
24                 },
25                 mapTypeId: google.maps.MapTypeId.ROADMAP,
26                 streetViewControl: false,
27                 styles: [{featureType:"administrative",elementType:"labels",stylers:[{visibility:"off"}]},{featureType:"landscape.natural",stylers:[{hue:"#0000ff"},{lightness:-84},{visibility:"off"}]},{featureType:"water",stylers:[{visibility:"on"},{saturation:-61},{lightness:-63}]},{featureType:"poi",stylers:[{visibility:"off"}]},{featureType:"road",stylers:[{visibility:"off"}]},{featureType:"administrative",elementType:"labels",stylers:[{visibility:"off"}]},{featureType:"landscape",stylers:[{visibility:"off"}]},{featureType:"administrative",stylers:[{visibility:"off"}]},{},{}]
28               
29             };
30             
31             // Force the height of the map to fit the window
32             this.$el.height($(window).height() - $("header").height());
33             
34             // Add the Google Map to the page
35             var map = new google.maps.Map(document.getElementById('map_canvas'), myOptions);
36             
37             // Bind an event to add tweets from the collection
38             
39             this.collection.bind('add', function(model) {
40                                        
41                 // Stores the tweet's location
42                 var position = new google.maps.LatLng( model.get("geo").coordinates[0], model.get("geo").coordinates[1]); 
43                   
44                 // Creates the marker
45                 // Uncomment the 'icon' property to enable sexy markers. Get the icon Github repo:
46                 // https://github.com/nhunzaker/twittermap/tree/master/images
47                 var marker = new google.maps.Marker({                         
48                   position: position,
49                   map: map,
50                   title: model.from_user,                  
51                   //icon: 'images/marker.png', 
52                   description: model.text
53                 });
54                 
55                 
56             });
57         }
58     }); //-- End of Map view
59 
60 }); //-- End of main function

A lot going on there, right? Let's take a look at everything going on.

  • Line 10: The el property is used to describe the html element associated with a view. In this case, we select the #map_canvas div tag.
  • Lines 14-31: Standard Google Maps API stuff. This is where the map gets generated. Note that we set the height of the map_canvas to the height of the window to make sure the map is the correct size.
  • Lines 36-51: Here we add an event to add a tweet to the map every time the collection associated with this view adds a new record.

** UPDATE :** Newer versions of Backbone will actively make a distinction between el and $el. Use el to access the raw HTML DOM object. Use $el to access the jQuery version, for example:

1 // Without jQuery:
2 this.el.text = "Hello world";
3 
4 // Use jQuery:
5 this.$el.text("I'm using jQuery!");

Initialize!

Finally, we instantiate the tweets collection and map view, setting everything in motion:

 1 $(function() {
 2 
 3 //... Mode, Collection, and View code ...//
 4 
 5 
 6 //-- Initialize ---------------------------------------------------------------//
 7     
 8     // Create an instance of the tweets collection
 9     var tweets = new Tweets({
10         model: Tweet
11     });
12 
13     // Create the Map view, binding it to the tweets collection    
14     var twitter_map = new Map({
15         collection: tweets
16     });
17 
18 });

Cool, a TwitterMap.

Pop open the browser and take a look at your freshly created TwitterMap! We could use the Twitter API to make the tweets appear at an exceptionally faster rate. However you really can't beat the simplicity of the basic search.

Conclusion

I'm not the first person to write a tutorial on backbone to say "But we've only scratched the surface", and I certainly won't be the last. Future installments will delve into a more detailed overview of how models and collections work. We'll go through events, syncing information, and other important concepts. I look forward to showing you what can be done within Backbone!

1 Taken from the documentation. It can be found here: backbonejs.org.

2 _.filter() is one of many exceptional functions provided by Underscore for sifting through data.