November 14, 2011

Single page apps are great solutions for data journalism. By offloading the complexity from backends and servers, journalists can build rich programs and graphics out of just Javascript, HTML and CSS. In fact, these “backends” can shrink to a vanishing point. We can use Twitter in place of a database. Or we can get even simpler and store (static) data in JS/JSON/XML files.

We can make news apps without having to touch a server or write any Ruby, Python or PHP. This is important. It allows data journalists to focus on developing their stories instead of configuring servers. The time and effort to launch an interactive application is reduced to the point where it becomes feasible for journalistic outlets of all sizes to make applications for both long-term pieces and breaking news.

Using JavaScript frameworks to manage one-page apps

There is something of a disconnect between traditional software development models and those of deadline-driven news. In a more server-side oriented development scheme, we would write a program on our computers, set up a server somewhere, configure it to run the app, transfer the data to some database on the server, make sure it can handle the load of a lot of people looking at it and then finally release it. In the newsroom, we have limited time.

Enter the Javascript app. The browser, where Javascript runs, doesn’t need to be set up by the app developer. It’s already there, for better or worse. This means less time spent, less hair-pulling and faster time lines between news and product. It just isn’t practical to spin up new servers and systems every time you need, say an interactive time line.

Why not just use Javascript? Well, it’s not always easy to organize a full app in just plain ol’ Javascript. The language, glacial in its development, lacks many features of more robust languages such as Ruby or Python. Often in interactive journalists’ quest for “a simple Javascript app,” they end up with a tangle of JS code. Fortunately, there are mature Javascript frameworks to help you avoid the mess. While they’re not as expansive as the Rails, Djangos and Struts of the world, JavaScript frameworks are great for managing one-page apps. One such framework is Backbone.js. I used it extensively while I was at Talking Points Memo.

What is Backbone?

Backbone is a MVC (Model-View-Controller) style framework for Javascript applications. (That’s only partly true — it self-admittedly and intentionally mixes Controllers and Views a bit — but that isn’t important right now.) MVC is a programming pattern in which your code is organized into, you guessed it, models, views and controllers.

Models are your structured data, views are the displays of that data, and controllers route user actions to views or changes in views. The rationale behind the MVC pattern is that by separating how the program works internally from how the user interacts with it, everything becomes easier to maintain.

Why should you, as a journalist, care about programming patterns? Programming patterns exist so that we rely on the speed and convention of previously curated choices. Rather than reinventing the wheel every time a new graph or visualization is needed, we can fall back into the comfort of the pattern and focus on the new details. By familiarizing yourself with MVC, you can immediately think of decomposing your story into the data, how it will look and how users will interact with it.

Building an app using Backbone

Now, let’s pretend for the rest of this exercise that you are an industrious journalist who wants to make a simple app to visualize the results of the hot, new public policy poll. You want users to be able to click on a candidate to toggle the visibility of their result, and you want users to add comments about each candidate. It’s a contrived app, yes. We won’t actually build the entire app in this tutorial, but we’ll show how we would set it up in Backbone.

Say your data looks as follows:

[{“name”:”Obama”,
“pct”:60},
{“name”:”Perry”,
“pct”:30},
{“name”:”Bachmann”,
“pct”:10}]

The data for each candidate will be represented by a model that will include his or her name, poll percentage and any comment the user types. There will be a view for each model that features a box containing the candidate’s name, a big red poll number and any comment that has been entered. The controller will route user actions and events to changes in the model and view.

The discreet charm of Backbone ease

First, we download all the dependencies and Backbone.js. We will include these on our page. Then we make a new file, “pollfun.js”. At the top of the file we will write:

Pollfun = {
data: [{“name”:”Obama”,
“pct”:60},
{“name”:”Perry”,
“pct”:30},
{“name”:”Bachmann”,
“pct”:10}]
}

All the rest of our code will follow. Take a moment to smile at the realization that this is the only data source this app will need. No database, no server, no backend. Lean, elegant.

Let’s make some models

In Backbone, we create models by “extending” the appropriate Backbone object. Don’t worry if you don’t entirely understand what that means for now. Essentially, we are just saying make a new thing called “Candidate” that has “Backbone.Model” as a starting point. We will just define an “initialize” method to set up the model when we pass the constructor some data. By default, whatever function is named “initialize” will be called when a new instance of the model is created. We could add as many methods as we want to the model but, for simplicity’s sake, we will stick with “initialize.”

Pollfun.Candidate = Backbone.Model.extend({
initialize: function(){
this.set({comment:””});
}
});

This code simply says: “Make a new model called Candidate and when an instance of that model is made, set its comment attribute to an empty string.” Now, Backbone provides a “defaults hash” that we can define in our model to serve the same purpose. I wanted to illustrate, though, how the constructor function works.

The view

Views are defined very much like models in Backbone. We will be defining some additional methods on our views to handle user interactions and the rendering of the view.

Pollfun.Candview = Backbone.View.extend({
template: _.template(“<h1 class=\”name\”><%=name%><\/h1> <h2 class=\”score\”><%=pct%><\/h2><p><%=comment%><\/p>”),

render: function(){
$(this.el).html(this.template(this.model.toJSON()));
}

events: {
“click .name”: “toggleScore”
},

toggleScore: function(){
$(“.score”,this.el).toggle();
}

})

There’s a lot going on here, but it’s simpler than it looks. First, we define a “template” method. By passing an ERB-style template to the underscore.js function “_.template,” we create a function that can render out the model’s data. An ERB template looks basically just like HTML with some funny percent and equal sign characters that allow you to pass values into the template to be rendered. You can read more about ERB here.

You’ll find the render function in almost every Backbone view. This function puts a few conveniences of Backbone to use. First, we see “this.el.” In Backbone, all views have an “el” attribute which contains the DOM element (the part of the Web page, perhaps a div or a span) to which the view is attached. We then wrap that DOM element as a jQuery object and replace its contents (with the html function of jQuery) with the rendered view. “Template” is a function that takes an object and spits back a rendered view of it. Here, it gets the attached model.

In Backbone, just like we saw with “el,” we get an attribute “model” for free. This contains the model attached to the view (if there is one). The “toJSON” function converts it from a Backbone model to a traditional Javascript object. The template function will fill in the corresponding attributes — the name attribute into the <%=name%> part of the template, for example — and return the rendered body of the view.

Finally, we have an events hash. This acts much like a controller. It says, “whenever a user clicks on a .cand_name element in a Candview, call that view’s toggleScore function.” toggleScore shows a hidden score, hides a shown score. In other words, it toggles the score.

Putting it together

Now that we have our models and views defined, let’s make a few instances of the model and set it so that each view re-renders whenever its model changes:

_(Pollfun.data).each(function(candidate){
var candidate_model = new Pollfun.Candidate(candidate),
candidate_view = new Pollfun.Candview({model:candidate_model,el:some_existing_element_on_the_page});
candidate_model.bind(‘change’,candidate_view.render);
})

This loops through our data and makes a model and a view for the candidate. To create a new instance of something, we write “new,” followed by its model. By passing an object — in this case a hash with a name and a pct, to the constructor — Backbone will set the values for us accordingly in our new instance of the model. That is to say, candidate_model will have “name” and “pct” attributes. Then we create a new Candview. We pass it the instance we just created of Candidate to attach the model and we pass it an “el” as well. “some_existing_element_on_the_page” is just a placeholder for the name of the element we want to attach the view to.

Finally, we call “bind” on the model’s instance. This invocation says, “when this instance of the model changes, call the render method of its view.” By binding the model and view in this way, we don’t have to worry about telling our page to update. Say we change a comment in a model. Without Backbone — or a similar setup — we would have to specify in our JavaScript that every time we save a comment, the code should fill that comment’s text in some element on the page. We would have to do this for every kind of user interaction and for every attribute of our data. Though this might not be terribly difficult to do if there is only one thing to change, it gets messy fast. Think of a one-page app where users can annotate results, rearrange candidates, display bios, pull in historical poll data, etc.

Very quickly, it becomes impractical to write specific handlers to tie every type of interaction to the desired result on the page. Instead, in Backbone, every interaction changes the model, the underlying data. Every time the data changes, Backbone emits a signal to all the views that are bound to it, telling them to re-render themselves with the new data.

What’s more, we have the added benefit of pre-defined methods to access parts of the data. We don’t have to use jQuery to scrape the page to get a current view of what data has been entered. (How many times have you written “value = $(el).text()” just to find out what values you have alrady set?) Instead, we can just directly ask the instances of the models. This makes sending our data back to the server much easier.

Premature conclusion

Now, we haven’t created much of anything. I’ve left the HTML up to the readers’ imagination, and there needs to be some code written to update the comment attribute of our instances. We need inputs. But I will leave that as an exercise. This piece was intended more as an introduction to Backbone than a full tutorial. There are many full tutorials out there.

There are far more features to Backbone than we covered. Some may argue that we skipped most of the neat parts of Backbone. I would contend that it is precisely the normalcy of much of Backbone that makes it special. Unlike, say Rails, Backbone doesn’t have many features that “automagically” transform your code. But this is a good thing.

Backbone is meant to be a small, utility framework that allows you to focus on app-building rather than Javascript book-keeping. There’s a lot of cool stuff in Backbone, and it’s thoroughly and painstakingly documented. I encourage you to immerse yourself in it.

This story is part of a Poynter Hacks/Hackers series featuring How To’s that focus on what journalists can learn from emerging trends in technology and new tech tools.

Support high-integrity, independent journalism that serves democracy. Make a gift to Poynter today. The Poynter Institute is a nonpartisan, nonprofit organization, and your gift helps us make good journalism better.
Donate
Erik Hinton is an interactive news developer at The New York Times. Before that he worked for Talking Points Memo where a great cast of…
Erik Hinton

More News

Back to News

Comments

Comments are closed.