Why your Javascript apps need more structure

Max Williams

This post was originally published on the New Bamboo blog, before New Bamboo joined thoughtbot in London.


There are several aspects to building javascript UIs, and you need to do a bit of planning beforehand if you want them to be successful. 2010 is set to be the year of Javascript’s ascendancy, and you can’t ignore it. You also need to start using it properly (if you aren’t already). We have evolved beyond the level of chucking event handlers inline in views and it is not possible to just hope for the best as things get more complicated.

I have divided this up into a set of best practices which I try to follow:

  • Adopt a structure early (MVC)
  • Think about models properly
  • Control flow in a declarative way (use Sammy)
  • Use views to encapsulate the logic for different parts of your application
  • Communicate between ‘layers’ via custom events

Another thing before diving into this. Think carefully about the target users. The use-cases I am exploring generally refer to apps rather than websites. Does your app need SEO? Do visually impaired people need to use it? Are people with “not-so-smart” phones going to be a concern? If any of these are important, make sure you don’t build an entirely JS UI, and instead build HTML which can degrade gracefully using normal http requests.

I have recently been making applications with Canvas and Sockets, and can’t necessarily rely on the server to generate html for me. Some of the following is based on these assumptions, and I don’t believe that everything you build should have this much structure.

I will however attempt to lay out what I see as a new structure for JS applications. If you’re keen to know what this can look like, see this code sample from the demo app.

It is also worth noting that I am not trying to provoke a flame war here, but rather relaying recent experience. People may disagree, and I’m keen to hear other opinions.

Adopt a structure

You can usually get away with slowly increasing the complexity of your JS application until you need to refactor, but it is usually best to start off with a basic structure first.

I am going to suggest adopting the MVC pattern which has been made famous by Ruby on Rails in webby circles (though has existed elsewhere for years). It is a good pattern, and the ideas which make it good for servers can also be applied to GUI programming.

Several JS frameworks have attempted to give JS apps structure, but I have never really been taken by them. I have the following constraints when choosing one:

  • it has to be solid, yet not overly complex
  • the code needs to feel like Javascript, not some port of a Java beast
  • the parts need to be understandable, and ideally interchangeable
  • using existing code beats writing everything from scratch

Sproutcore and Capuccino stop the code feeling like Javascript anymore, and GWT is too linked to Java backends.

There are also some more lightweight frameworks such as Javascript-mvc, which wasn’t quite right for me. The Kiwi framework came the closest to what I wanted, and served as a bit of an inspiration for me. In my case, it still wasn’t quite right though, hence taking a more polyglot approach.

The primary things to plan are the following:

  • Models define the domain objects in your application and can be synced with the server via Ajax or other methods. They often map to database tables, but you shouldn’t be constrained by this. If you are using REST, hooking them up with your app should be a doddle.
  • Views represent parts of your GUI which you can interact with. They are fed by data, and need to respond to changes in the data.
  • Controllers are the intermediaries between model data and the GUI elements which control them. In a JS app, your controllers manage the changing state of the views as a whole.

I tend to create directories for these such as /lib/views and lib/models. Controllers are different and (as I will explain below) sit in a routes.js file which holds all my Sammy routes.

Since a UI maintains state - unlike most web applications - you can then use events to communicate between the layers.

Models

You need to think about the Models in your app both on the level of the individual instances, and as collections of instances. The reason these collection objects are needed as well as model objects is because these act as factories. Usually (e.g. in Ruby) this would be taken care of by the class, which itself is an object, but javascript doesn’t really have classes as such, so you need a collection object to take its place.

These data sources are used by Views to, for example, populate a list of people in your account. Additionally these individual instances will have come from rows in your database, and will therefore need to be created and updated.

Our very own Mr Pickles has recently been working on a library to deal with this, which he promises to blog about shortly. I won’t therefore go into much detail on it, and will be using more basic examples in my examples.

Views

There is no definitive way to represent views in a JS app, but I tend to make a class for them, and pass in a DOM element which exists on the page. I might therefore have objects similar to the following:

var ProjectPicker = function(elem, projects) {
 var elem = elem;
 var projects = projects;

 var draw = function(){
   for (i in projects){
     elem.append('<p>'+project.name+'</p>')
   }
 };

 draw();
}

These views are initialised on page load, passing in the data which they might need to render, eg:

var project_picker = new ProjectPicker($('#project_picker'), projects);

If the collection of ‘projects’ in your app changes, a ‘redraw’ method can be created which uses the project list for input. There are many advantages to using a system like this, as View objects can handle the manipulation of their particular bit of the DOM in response to changes in your datasets. More about this below in the Events section.

While I have simply injected HTML into the page as a string, there are several better techniques for doing this. Many templating languages have been created, such as the one included with Sammy. Personally, I like to replicate html which exists in the HTML, and let the server do it in one place. What is really bad is when you emulate html created by the server in your JS, and introduce the possibility of inconsistencies.

I am also not really fond of servers sending little bits of HTML to update the page (as is sometimes popular with RJS and the like), and believe that a JS app be feed data which it manipulates itself.

Controllers

First off, use Sammy. It is really awesome, and allows you to reach new heights of declarative page state changes.

If you want to show a form to create a new project, make a link to #/projects/new which tells your “new project” View to show itself. In this example I am just manipulating the DOM directly, but you could go via your views, or trigger events:

get('#/projects/new', function() { with(this) {
  $('#project_form').show();
}});

The data from this form is posted to ‘#/projects’, where you handle your data syncronisation, and then update your views based on the results.

post('#/projects', function() { with(this) {
  Projects.create( { name: params['name'] } )
  redirect('#/')
}});

This is immensely powerful, and I have not done justice to it here. I have written more on a separate post about sammy.

Custom Events

These are really the heart of building great extensible UIs. This means utilising the Observer pattern in your objects. I also like to think of it in pubsub terms: a custom event has 2 parts, the trigger (publisher), and the events which are bound to it (subscribers).

Create a convention for events which you are going to stick to. I like to link them to my model names. Hence:

$().trigger('Project-create', [new_project])

might be bound to this in my project list view:

var ProjectPicker = function(elem, projects) {
 ...

 var add_project = function(evt, project){
   elem.append('<p>'+ project.name + '</p>')
 }

 // EVENTS
 $().bind('Project-create', add_project)
}

You often need to cascade events, so that the listener for 1 event makes a change and then publishes a new event. It is cool.

When you use or create a decent library for syncing your Models via Ajax, you can add callbacks based on your conventions so that a successful/unsuccessful request to your server triggers the following:

//Create:

'Object-create', object
'Object-create-error', object.errors

//Update:

'Object-update', object
'Object-update-error', object.errors

//Destroy:

'Object-destroy', object
'Object-destroy-error', object.errors

A brief illustration of these principles

I have made a little demo app, to show how these principles can be used. It doesn’t use Ajax, but shows you the points where it could be added. This app is far from perfect, and I don’t want to suggest that the way I chose is the only one. But I think the code shows what I’m trying to get at.