This article provides a brief introduction to JavaScript AMD and MVC technologies. It then details a working example of modularizing the Backbone.js 101 tutorial, “Todos” by Jérôme Gravel-Niquet, using Require.js. The article assumes basic familiarity with the aforementioned libraries, concepts, and example. This will be part one of a four part series. If you already know the basics or would like to dispense with the pleasantries then skip ahead to Let’s Get Started.

Introduction

If you have ever created a large JavaScript application then you know that over time, despite the best of intentions, that it can become quite unmanageable. There are a variety of reasons for this phenomenon, but in an ideal world this can be prevented by a well-structured application with adequate dependency management. Being organized is not a radical idea, but finding the right solution is sometimes not easy because first you have to identify the reasons for this gradual slide into code mayhem. In my opinion there are two main issues that need to be addressed.

  1. Code needs to be sectioned off into the standard model, view, and controller pieces. I am not going to get into why. Just trust me.
  2. Dependency management has always been difficult in JavaScript. There are number of approaches that range from loading the kitchen sink to loading dependencies just in time.

I have used many homegrown solutions over the years to address these issues, but as of date these libraries are my favorite.

MVC: Backbone.js

If you do not think MVC (Model View Controller) is a good idea then this article is not for you. I am not going to cover the benefits of MVC, that is another article in itself, but MVC === GOOD. My favorite JavaScript MVC library is Backbone.js. It is light-weight and flexible. It is not bloated like most MVC libraries or rigidly structured, which allows you to creatively and efficiently organize your code in many different ways. The amount of abstraction and structure is just right. Thank you Backbone.js.

AMD: Require.js

Web applications are large complex beasts, which have grown up in an environment that was not designed to handle the numerous overlapping and nested dependencies now required. As a result there have been many hacks and solutions to handle dependency management. I have written custom script loaders and require wrappers for code blocks, but none of the solutions have been as elegant, flexible, or scalable as I would have liked. Smarter and more talented people have written better solutions, but for the most part they are robust script loaders that do not fully address the asynchronous nature of the web. Enter AMD, Asynchronous Module Definition. I am not going to get into the benefits of AMD. Again, trust me it is good. If you don’t trust me then do some research and either trust yourself (if you come to the right conclusion) or others on the web. For instance, Dojo recently adopted AMD. Even the almighty jQuery has been modified to be AMD compliant – comment from 1.7.1, “// Expose jQuery as an AMD module”. In this article I will be using Require.js for AMD. It’s better than bad, it’s good. It has a number of features including a build process. Additionally, James Burke, the author, is very responsive and overly devoted to the project, so much so that I pity his pets and plants.

Let’s Get Started

First a few disclaimers… AMD is supposed to help limit the amount of global pollution via closures. In this example the majority of the bootstrap files are libraries that end up in the global namespace. I even explicitly move Underscore to window because Backbone expects it to be there. As of writing James Burke (Require.js author) and the Backbone folks are sorting out the best way to make Underscore and Backbone AMD compliant. James has already forked Backbone and created an AMD compliant version. That being said I am comfortable with my bootstrap libraries living in window. It seems easier than explicitly requiring such frequently used core files for each module. In theory this makes refactoring and versioning more difficult, but in reality if I am removing/changing anyone of these libraries then I am doing some major refactoring anyhow, so the ease of use outweighs the evils of global pollution. Next disclaimer, this tutorial is based off of POC work that I have been doing recently. I am certain that I am doing something incorrectly and that someone else has thought of a better way. Phew. That was a lot. If it is not obvious yet – I am bit sensitive and defensive.

The completed code for the example below can be downloaded at GitHub. Let the fun begin.

Step 1: Include Require.js

First thing I did was eliminate all script tags from index.html – json2.js, jquery-1.6.4.js, underscore-1.2.2.js, Backbone.js, backbone-localstorage.js, and todos.js. Next, I added a new script tag before the body close tag:

<script data-main="main" src="require.js"></script>

This tag includes Require.js and tells it to load ‘main.js’, which is going to bootstrap the application. The fact that it is named main.js is not important. It just sounds cool.

Step 2: Bootstrapping, main.js

This is what gets the party started. First thing to note is that everything is wrapped in an immediate function. I did this so that I can scope variables and ‘use strict’ without impacting any other code. Why ‘use strict’? JavaScript will not warn you when you try to do something stupid. I am not very smart, so I often do stupid things. ‘use strict’ will cause some things to fail noisily, so that I can easily spot my errors. Next I create a configuration variable for mapping paths, so that I do not clutter up require statements with long repetitive strings. This is one of the many configuration options that Require.js provides. After that I create references to require statements with contexts and the path mappings. Now Require.js works its magic. The first require statement loads Underscore.js. I did not use the version from the Backbone.js examples because it would not load as an AMD module, but the latest version would. This is where things get a bit hacky. Out of the box Backbone.js depends on Underscore.js and expects it to be a global variable, so I have to load Underscore.js first and copy it to window. Once this is done I can then load Backbone.js and all the other core libraries. One thing to note is the ‘order!’s in the second require. This instructs Require.js to load these modules in order. Order.js is a Require.js plugin. Require.js will load the plugin automatically when it sees the ‘order!’ declarations. Finally, there are a bunch of placeholders (a, b, c, etc.) for the libraries that I do not care about. The only module that I want a reference to is the application module, which I then use to initialize the application.

(function () {
    'use strict';    
    console.log('go go gadget main!');
    
    var config = {}, underscore, bootstrap;
        config.paths = {
            'vendor' : '../../test/vendor',
            'root' : '../..',
            'examples' : '..'
        };
    
    underscore = require({ context : 'underscore' });
    bootstrap = require({ context : 'bootstrap', paths : config.paths });
    underscore(['underscore'], function (_) {
        window._ = _; // move underscore to global namespace for backbone 
        bootstrap(['vendor/json2', 'order!vendor/jquery-1.6.4', 'order!root/backbone', 
			'order!examples/backbone-localstorage', 'order!todos.js'], function (a, b, c, d, app) {
            app.init();                
        });
    });    
}());

Step 3: The Application Module

If you are still with me I apologize for the last section. It was too long. The code snippet below is essentially todo.js wrapped in a define statement. All I did was move all the global variables to local scope and have the module return an object with the init method. The init method simply returns a new AppView. In reality nothing much was gained by this refactoring. The only interesting piece is the layer of abstraction I created between the Backbone code and the app.init() call in main.js. I did this because it cleanly separates the technology used in the module with the application layer code. It is difficult to see the importance of this layer in an application this small, but loosely coupled layers have always served me well in the past in terms of maintenance, refactoring, and scalability.

// An example Backbone application contributed by
// [Jérôme Gravel-Niquet](http://jgn.me/). This demo uses a simple
// [LocalStorage adapter](backbone-localstorage.html)
// to persist Backbone models within your browser.

// Load the application once the DOM is ready, using `jQuery.ready`:
define (function () {
  var Todo, TodoList, Todos, TodoView, AppView, module = {};

  /* I cut out all the Backbone code. All I did was move gobals to local scope 
   * and add an object that gets returned by this module. The full code can be viewed
   * at https://github.com/jstrimpel/backbone/blob/master/examples/todos-requirejs-1/todos.js.
   */

  // Finally, we kick things off by creating the **App**.
  module.init = function () {
    return new AppView;          
  }
  
  return module; 
});

Wrap Up

This article was not very useful other than as a stepping stone for things to come. Things will get more interesting in the next three installments – I hope. The main point was to introduce and marry two excellent libraries to solve a couple major issues that plague all large JavaScript applications.

Up Next

Below are the coming attractions. Same bat channel. Same bat time.

Discussion
redCashion 1 comment Joined 01/12
01 Jan 2012

This was a really helpful and timely post for me. Thanks so much and looking forward to part 2!

yottadata 7 comments Joined 10/09
15 Jan 2012

Hi Jason, you mention that the article "provides a brief introduction to JavaScript AMD and MVC technologies", but I'm wondering about the higher level "why" as I have no idea what Javascript AMD is.

Is the goal to show a way to build web apps with an MVC architecture, and what is the relationship to Teradata ? I'm assuming you'll hook that in in the later sections.

If you could perhaps add a "Why or how I would use this" would help understand the context. Thanks.

JasonStrimpel 5 comments Joined 09/11
16 Jan 2012

@yottadata – Below are some links describing AMD and the why. A direct relationship to Teradata does not exist unless you are developing portlets for Viewpoint. The article is intended for Front-end developers in general, which I admit is a bit confusing since all other blogs on DevEx are specific to Teradata. The primary intent of the article is to describe how to create scalable, well performing JavaScript Centric web applications by combining to two JavaScript libraries.
http://requirejs.org/docs/whyamd.html
https://github.com/amdjs/amdjs-api/wiki/AMD
http://dojotoolkit.org/blog/learn-more-about-amd

You must sign in to leave a comment.