A simple Backbone.Marionette tutorial

Friday 10 January 2014

This tutorial will show you how to use Derick Bailey’s Backbone.Marionette to build a simple application. But beyond getting a simple walkthrough, you’ll also be able to compare the app we write to the original, and see how Backbone.Marionette makes your life easier when writing Backbone apps.
So what’s the app going to be? It will simply allow cats to be ranked within a list: click “up” the cat is more popular, click “down”, the cat is less popular. Simple, no? Besides, on the internet, everybody likes cats ;-) If you want to skip ahead and checkout the code, it’s onGithub and you can see it in action on this jsFiddle (the image assets have been replaced with placeholders). You can also follow along by checking out out each git commit.Update (June 2013): I’ve written a book on Marionette. It guides you through developing a complete Marionette application, and will teach you how to structure your code so large apps remain manageable.
Also, since this tutorial was written, Marionette has evolved with new functionality and better techniques to write scalable javascript apps. These are naturally covered in the book.

First steps

Since this is a tutorial, we’ll start with all the necessary static assets already in place (CSS, libraries, etc.). So go ahead and grab the zipped archive containing all you need to get started, or get the initial commit from github.
The first thing we’ll do is include our application within a javascript file that we include in index.html. Within that file, we’ll start a Backbone.Marionette application, and add a region to it (code):
1
2
3
4
5
MyApp = new Backbone.Marionette.Application();
 
MyApp.addRegions({
  mainRegion: "#content"
});
This region corresponds to an area that we will use in our application. Since our application is simple, we’ll be using only one. However, more complex apps will be able to use multiple regions to better breakdown the application’s complexity, using e.g. one region for the navigation menu, another for the main content, etc. The way we set the region is by passing a jQuery selector to the area we want to use. In this particular example, we want to use the DOM element with id “content” (which you can see in the index.html).
So now that we have a region to display things, what are we going to display? I mentioned it earlier: a leader board for cats. Which means we’re going to need to store information about cats, but also that we’re going to be dealing with multiple cats…

Models and collections

Backbone was designed mainly to build RESTful javascript apps, and it follows this RESTful architecture on the front end: we’re going to store our cats in Backbone models. Although we’re not going to be communicating with a server in our case, Backbone provides handy mechanisms to store model modifications on the server: Backbone.sync allows you to (e.g.) call cat.save() and the cat’s JSON representation will be sent to the server for storage.
So what do these models look like? We’ll, let’s go ahead and create a model for our cat (code):
1
AngryCat = Backbone.Model.extend({});
The code is deceptively simple: all we need to do is extend a Backbone class, and we get lots of goodies for free… As you can see, when you extend a Backbone Model, you need to provide a javascript object. For now, we’re simply providing an empty object with “{}”.
Remember how we’re going to deal with multiple cats? We’ll need some construct to deal with that, and that’s where Backbone collections come in. Collections also provide many niceties, some of which we’ll cover later. Let’s write the collection to store all of our cats (code):
1
2
3
AngryCats = Backbone.Collection.extend({
  model: AngryCat
});
This time you’ll notice that we didn’t simply provide an empty object when extending Backbone’s Collection. The reason is that a Backbone collection needs to know what king of models it contains. In our case, the collection contains cats, so all we need to do is declare our AngryCat model within the object we use to extend.
Now that we have everything in place to start managing our cats, we still need to display them.

Views

What are views in Backbone? Well, for starters, they’re very different from what you might have encountered in other frameworks (such as Rails). In Rails, views simply generate HTML that gets sent to the client. In Backbone, we use template for that role, and views act more like observers: they monitor changes in the applications state (data changes to models, user interactions, etc.) and update the view accordingly.
The only view mechanism Backbone provides is the plain vanilla Backbone.View. Backbone.Marionette, on the other, provides several different view types to choose from.  Since we want to display a collection of cats, we are going to use Backbone.Marionette’s CompositeView and ItemView. I’ll briefly cover both of them, but Derick Bailey (Backbone.Marionette’s author) covers devoted a blog post to the topic.

Backbone.Marionette.ItemView

The item view is, according to the documentation, “a view that represents a single item”. In our case, we’ll use it to represent a single cat in our collection. Since we’re going to display all of our cats within a table, this item view will essentially render a table row.

Backbone.Marionette.CompositeView

We’re going to use this view to render our collection. We already have an ItemView to render a single cat, so this view will render the table (headers, etc.) and then add one row for each cat in the collection by rendering the ItemView.
Backbone.Marionette does a lot behind the scenes for us. By using the composite view to render the collection, we don’t need to worry about updating the view when our collection changes (if we add a model instance, for example): the CompositeView does that on its own.
It you’ve glanced at the docs, you might have noticed the Backbone.Marionette also provides a CollectionView. If we’re going to display a collection, why not use that? Well, the simple answer is that the collection view is designed to simply render the collection (i.e. render one ItemView per model), without any fluff. We want to go slightly further, since we want the collection to have surrounding HTML (namely the table tags, headers, etc.).
So now we know what we’re going to use to display our collection. But how are we going to generate the HTML proper?

Templates

To generate HTML, Backbone uses templates: HTML skeletons that we will fill in with data from a model (e.g.). Templates would be the Backbone equivalent to the views Rails uses.
Let’s start with the template for the entire collection, which will be a table (code):
1
2
3
4
5
6
7
8
9
<script type="text/template" id="angry_cats-template">
  <thead>
    <tr class='header'>
      <th>Name</th>
    </tr>
  </thead>
  <tbody>
  </tbody>
</script>
You can see that this template is simply put within the HTML body using a script tag that has a content-type of “text/template”. The reason for doing so is that the DOM element will be easily selectable using jQuery (which is why the template has an id), but the content-type prevents the browser from doing anything with it.
This template will enable us to have a table with a single column containing the cat’s name. So now, we can move on to writing the template used to create individual rows (code):
1
2
3
<script type="text/template" id="angry_cat-template">
  <td><%= name ></td>
</script>
Same concept here as above: a script tag with an id and a special content-type. The addition you see here are the “<%=  %>” tags which will be familiar if you’ve used ERB before: they interpret the code between them. In this case, we’re simply going to print the “name” attribute of the model that is passed to the template.
Now that we have our templates, let’s write the view code that uses them.

Back to views

First, we’ll write the ItemView that will display a single cat (code):
1
2
3
4
5
AngryCatView = Backbone.Marionette.ItemView.extend({
  template: "#angry_cat-template",
  tagName: 'tr',
  className: 'angry_cat'
});
We need to specify a few things when declaring the ItemView. First, which template we’re going to use (which is a mandatory requirement). Then, we can specify some (optional) properties to control the HTML that gets rendered.
At this point, let’s take a small break to Backbone’s rendering. When a view is rendered, Backbone will create a DOM element that you can then manually insert into the document. If you look at a few Backbone tutorials, you’ll typically see that at work in a view’s render method.
Backbone.Marionette does a lot of cool things for us, and as a consequence our ItemView doesn’t have a render method visible. In other words, we haven’t had to declare a render method, because Backbone.Marionette already does that for us, and the created DOM element will get added to the document with our CompositeView.
So now that you know some of what goes on when a Backbone view gets rendered, the next properties will make more sense to you. The className is simply an HTML class that gets added to the element Backbone creates, so our item will have “class=’angry_cat’” in its HTML tag.
Speaking of the HTML tag, the plain vanilla tag the Backbone uses when creating an element during a view render is a “div”. Since we’re going to display our cats within a table, that wouldn’t suit our needs: we need a “tr” tag to be generated as the DOM element instead, and that is what we demand with the “tagName” property.
Now, onto the CompositeView (code):
1
2
3
4
5
6
7
8
9
10
11
AngryCatsView = Backbone.Marionette.CompositeView.extend({
  tagName: "table",
  id: "angry_cats",
  className: "table-striped table-bordered",
  template: "#angry_cats-template",
  itemView: AngryCatView,
 
  appendHtml: function(collectionView, itemView){
    collectionView.$("tbody").append(itemView.el);
  }
});
The same properties are being defined as for the ItemView, with the addition of “itemView”. Since our CompositeView will be rendering a collection, we are telling it which ItemView to use to render each model within the collection.
In addition, since we aren’t just going to be dumping the generated HTML at the end fo the template, but in the middle, we have to specify how HTML should be instered into our template with the appendHtml function. As you can tell, we’re not asking for much: we just want it dumped within the “tbody”.
UPDATE: Since writing this post, Marionnette has been updated, and the CompositeView’s behavior has changed slightly. You now need to the view to rerender the portion displaying the collection when the latter is sorted. See this commit.
Now that we have templates and views defined, let’s display something already!

Initializers

Right after we start our app, we’ll want to have our list of cats displayed. For this purpose, we’ll add a simple initializer (code):
1
2
3
4
5
6
MyApp.addInitializer(function(options){
  var angryCatsView = new AngryCatsView({
    collection: options.cats
  });
  MyApp.mainRegion.show(angryCatsView);
});
This initializer will receive any options we send to our application when we call its “start” method (which we haven’t done yet). What our initializer does is straightforward: create a new view with our cats, and display it.

Starting the application

Pretty straightforward also (code):
1
2
3
4
5
6
7
8
9
$(document).ready(function(){
  var cats = new AngryCats([
    { name: 'Wet Cat' },
    { name: 'Bitey Cat' },
    { name: 'Surprised Cat' }
  ]);
 
  MyApp.start({cats: cats});
});
Once the DOM is ready, we create a collection of cats populated by cat models we create simultaneously. Then, we simply start our application by passing in the cats collection, and voilĂ ! The first milestone for our leader board is done (and should look something like this): we can display a list of models.

No comments:

Post a Comment

 

Total Pageviews

Blogroll

Most Reading