I recently gave a presentation about creating Single Page Web Apps and thought it would be a good topic to blog about as well. "What exactly is a Single Page Web App?" you ask. To put it simply a Single Page Web App is a website that attempts to recreate the experience of a native desktop or mobile application. On a Single Page Web App you are less likely to see page refreshes, the browser navigation within the App is often overridden and there will usually be some offline capabilities. To a user their interaction with a Single Page Web App will be almost identical to how they would interact with a full native application.

The rise in popularity of RESTful APIs and an ever increasing interest in delivering feature rich interactive experiences on the web have pushed the idea of Single Page Web Apps into the forefront of many web developer's minds. Luckily many of the new features in HTML5 and CSS3 have created an environment where it is easier than ever to create a website that replicates a rich native application.

My personal interest in developing Single Page Web Apps came about while researching options for the mobile website of a client. It seemed that the best user experience for this client's mobile website was to emulate the native mobile experience of their iPhone app. Although we did not end up implementing a true Single Page Web App, the research for that application showed that it would have been a good design option for them and for other clients with similar business needs.

Even though the new features in HTML5 and CSS3 have made it easy to replicate the behaviors of a native application on the web, a large portion of the code that would traditionally exist on the back-end will now be transitioned to the client-side in a Single Page Web App. What this can lead to is a mess of spaghetti code where business logic is interspersed with DOM interactions, ultimately creating an unmaintainable codebase.

What are some solutions to the problem of an unstructured client-side codebase? Many prominent web developers have created or recommend the use of a client-side MVC framework when developing rich interactive web applications. Some examples include, Backbone.js, Ember.js, Angular and JavaScriptMVC. Each of these have slight variations and some do not strictly adhere to the MVC design pattern.

In brief, MVC is a programming design pattern for separating the different concerns of your application. The M stands for Model and is where your business objects and state will reside. The V is for View and it is where all of your display logic will reside. Finally, the C is for Controller which is what the user will be interacting with and what handles the interactions between your Views and Models. This is a rather simplistic explanation, but will work for the purposes of this blog post.

To help demonstrate what the structure of your application would look like using one of these frameworks I will now go through a demo using Backbone.js. We will be making a quick and dirty note taking app called AlwaysJot™. You can find all of the demo code here https://github.com/joshsticks/AlwaysJot. I chose Backbone.js for this demo because of my familiarity with it's creator Jeremy Ashkenas. He has worked on a handful of great JavaScript projects, including my personal favorite CoffeeScript. Backbone.js is currently in use at DocumentCloud, Hulu, LinkedIn Mobile, WalMart Mobile and many more.

Demo

First up, let's talk about Models in Backbone.js. A Model is a simple representation of the data of your application. In this case, a Note. To create a new Model one must first extend from Backbone.Model to create a custom Model that will be used to create individual instances of your Model object.

var Note = Backbone.Model.extend({});
var note = new Note({
	title: "Dr. Contact Info", 
	content: "Dr. Paul, 123 Fake St. Richmond VA 23219"
});

Next we will discuss Views. To create a view you extend from Backbone.View similar to how you created the Model. In Backbone.js a View is responsible for rendering out the data that is housed in your application's Models. Usually this will be done using a HTML templating library to create clean reusable markup. Backbone.js uses Underscore templates out of the box for this purpose, but you could just as easily use handlebars or another templating library.

var NoteView = Backbone.View.extend({
	tagName: "ul", //div is the default
	className: "box",
	template: _.template("

<%= title %>

<%= content %>

"), render: function () { this.$el.html(this.template(this.model.attributes)); return this; }, events: { "click" : "open" }, open: function () { AlwaysJotApp.navigate("note/" + this.model.id, true); } });

Thirdly we will talk about Collections. Collections are basically groupings of Models. Collections will usually define where the RESTful url is to GET and POST your data from and what the Model type is that they contain. This common behavior is done by overriding the url attribute and the model attribute when extending from Backbone.Collection.

var NoteList = Backbone.Collection.extend({
	model: Note,
	url: '/notes.json'
});

Finally there are routers which extend from Backbone.Router. A router's responsibility is to direct specific requests for urls to certain routes within the application and to keep track of a history that will override the base behavior of the navigation of the browser. This behavior allows users to more naturally navigate through the site like a native application rather than a series of html pages. Out of the box Backbone.Router uses the # symbol in the url to prevent different pages from loading when navigating via the browser's navigation buttons. There is also support for you using the new HTML5 PushState spec for those browsers in which it is supported. The best practice for building an app with a single router is to actually just create an instance right when you extend from Backbone.Router.

var AlwaysJotApp = new (Backbone.Router.extend({
	routes: {
		"":"index", 
		"note/:id": "show",
		"note/:id/edit": "edit"},
	initialize: function () {
		this.noteList = new NoteList();
		this.noteList.on('reset', function () {
			AlwaysJotApp.noteListView = new NoteListView({collection: AlwaysJotApp.noteList});
			AlwaysJotApp.noteListView.render();
			$("#app").html(AlwaysJotApp.noteListView.el);
		});
		this.noteList.fetch();
	},
	start: function () {
		Backbone.history.start({pushState: true});
	},
	index: function () {
		//default view
	},
	show: function (id) {
		var noteView = new NoteDetailView({model: this.noteList.get(id)});
		$("#app").html(noteView.render().el);
	},
	edit: function (id) {
		var noteView = new NoteEditView({model: this.noteList.get(id)});
		$("#app").html(noteView.render().el);
	}
}));

Some areas that I didn't touch on in this post are event handling within Backbone.js and using localstorage to make your app more functional when a network isn't present. Hopefully this discussion and brief demonstration has helped to show you the benefits that can come from leveraging a client-side mvc framework when developing Single Page Web Apps. Whether your app is targeting desktop browsers or mobile the benefits of cleanly structured client-side code are immense.