QUnit layout for JavaScript testing in ASP.net MVC3 Espresso Tip: Changing those nasty constraint names in Code First

BackboneJS modular app using RequireJS

On December 9, 2011, in BackboneJS, JavaScript, RequireJS, by jonathancreamer

Write the App

Everyone knows that large scale JavaScript applications can get out of control really quickly really fast. Tools like jQuery are helpful in providing easy access to the DOM, but provide little to no help when piecing together a full fledge application. You’ll begin writing a few selectors here and there, manipulating some data, making some Ajax requests, and next thing you know your knee deep in a big bowl of Spaghetti code!

Bring on RequireJS.

RequireJS is a framework that adheres to the ComonJS specification for Asynchronous Modular Definitions (AMD). See this post by a fellow brewer, Derek Greer for some more info on getting started with Requirejs.

The basic idea behind RequireJS and AMD is to separate your code into Modules, and by Modules, I mean this definition found by Nicholas Zakas…

Module: An independent self-contained unit of a spacecraft.

spacer

Here, each module can exist separately, or together with the rest of the space ship.

This is a common way to solve JavaScript architecture as well.

To build a BackboneJS app with RequireJS…

  1. Download RequireJS (I used the Sample Require + jQuery App)
  2. Download the Order plugin
  3. Download UnderscoreJS (Backbone depends on Underscore)
  4. Get one of the AMD versions of Backbone from @jrburke off of one of his optamd branches. I used this one https://github.com/jrburke/backbone/blob/optamd/backbone.js
  5. Create a folder structure, and files like… (Bold means create a new empty file)
    /r.js (Copy from the RequireJS download)
    /app/index.htm
    /app/scripts/
    /app/scripts/require-jquery.js (Copy from the RequireJS download)
    /app/scripts/main.js
    /app/scripts/app.js
    /app/scripts/order.js
    /app/scripts/lib/underscore.js
    /app/scripts/lib/backbone.js
    /app/scripts/routers/home.js
    /app/scripts/views/welcome.js
    /app/scripts/models/movie.js
    /app/scripts/collections/movies.js
  6. Build models, views, routers, and collections at your leisure

First up is the index.htm file…

<!DOCTYPE html>
<html>
<head>
<title>BackboneJS Modular app with RequireJS</title>
<script data-main="scripts/main" src="/img/spacer.gif"> 

Then there’s main.js. This is where you’ll define the base of how your application will load…

require.config({
    'paths': {
        "underscore": "libs/underscore",
        "backbone": "libs/backbone"
    }
}); 

require([
	'order!libs/underscore',
	'order!libs/backbone',
	'order!app'
	],
	function(_,Backbone,app){
          app.init();
});

The require.config set’s up some paths for the modules that are defined with a name. Next, the require function takes in 2 arguments. First is an array of dependencies that main.js will load in. The order plugin makes sure that Underscore and Backbone get loaded in order as Require typically loads things in asynch which is bad for Backbone since it requires Underscore. Using the require-jquery verion includes jQuery 1.7 which now has AMD built in.  app references the app.js file you will create in a second. The function callback takes the loaded dependencies and passes them as arguments in the order you loaded them in, _, Backbone, and app.

Now for the app.js

define(['routers/home'], function(router){
    var init = function(){
        console.log("App Started...");
    };

    return { init: init};
});

Here we use define to create a new module. The first argument is an array of dependencies, and the second is a callback like the one on main.js which will fire once the dependencies have loaded. For now I am just requiring the home.js router.

The home.js router looks like…

define(['backbone','views/welcome'],function(Backbone,welcome){
    var homeRouter = Backbone.Router.extend({
        initialize: function(){
            Backbone.history.start();
        },
        routes: {
            '': 'home' // Default route
        },
        'home': function(){
            welcome.render();
        }
    });

    return new homeRouter();
});

Here we load in Backbone, and the welcome.js view…

define(['jquery','backbone','underscore', 'collections/movies'], function($, Backbone, _, movies){
    var welcomeView = Backbone.View.extend({
        el: "#main",
        initialize: function(){
            this.movies = new movies();
            this.template = _.template($("#moviesTemplate").html());
            this.movies.bind('add', this.addMovie, this);
        },
        render: function(){
            this.movies.add({ name: 'Empire Strikes Back'});
            this.movies.add({ name: 'Jurrasic Park'});
            this.movies.add({ name: 'The Last Crusade'});
        }
        addMovie: function(model){
            $(this.el).append(this.template({ model: model.toJSON() });
        }
    });
});

This one loads in jQuery, backbone, underscore, and our movies collection which looks like…

define(['backbone','underscore','models/movie'],function(Backbone,_,movie){
    var movies = Backbone.Collection.extend({
        model: movie,

    });

    return movies;
});

From here we get down to the model, movie…

define(['backbone','underscore'],function(Backbone,_){
    var movie = Backbone.Model.extend({});

    return movie;
});

So that’s how to create a basic app with a router, view, collection, and model.

Build with Node or Java

One of the cool parts about Require is it’s ability to build and compress your application for you. It requires an app.build.js file, r.js(which you download with the RequireJS) jQuery app, and if you want to compile with Java, you’ll need Rhino and Clojure (Thanks to @jrburke again for this help page on r.js) or if you want to use NodeJS you’ll just need Node for your OSOC (Operating System of Choice) . The app.build.js file will typically look something like…

({
    appDir: "../",
    baseUrl: "scripts/",
    dir: "../../app-build/",
    //Comment out the optimize line if you want
    //the code minified by UglifyJS
    //optimize: "none",

    paths: {
        "jquery": "require-jquery",
		"underscore": "libs/underscore",
		"backbone": "libs/backbone"
    },

    modules: [
        //Optimize the require-jquery.js file by applying any minification
        //that is desired via the optimize: setting above.
        {
            name: "require-jquery"
        },

        //Optimize the application files. Exclude jQuery since it is
        //included already in require-jquery.js
        {
            name: "main",
            exclude: ["jquery"]
        }
    ]
})

This file is used to tell r.js how to build the application. There is help on requirejs.org/docs/optimization.html about the file. Basically r.js will look at the main module, and trace all of it’s dependencies, minify them, and combine them for you into one fun happy little minified package at your dir from the app.build.js file.

To run the optimizer in Java, go to the file above the root of app where r.js is located and use the command…

java -cp c:/path/to/rhino/js.jar;c:/path/to/compiler/compiler.jar; org.mozilla.javascript.tools.shell.Main r.js -o "c:/path/to/your/app/scripts/app.build.js"

If you are using node, then run this command…

node r.js app/scripts/app.build.js

And next thing you know, you’ll have an app-build folder with a shiny new minifed version of your app. The node optimizer is quite a bit faster than the java one, so I’d use that one.

So, that’s basically it! If you have any questions, let me know as I spent many hours figuring this thing out. The biggest help was when jQuery and UnderscoreJS both added AMD, and then once the Backbone AMD branches came out, it was much easier!

I have a simple working app called Savefavs up here at js.jcreamerlive.com and the source for it on Github. So take a look at that for a little more advanced stuff

I also have a Starter application here https://github.com/jcreamer898/RequireJS-Backbone-Starter that you can fork and use to build your own app.

If you enjoyed this article, please consider sharing it!
spacer spacer spacer spacer

Tagged with: amd • backbonejs • commonjs • module • requirejs • underscorejs 
  • Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1000

  • Anonymous

    Good, clear article.

    Might I suggest that you reference your constructors with a capital letter, like so:
    var Movies = Backbone.Collection.extend({});

    While not a requirement, Capitalizing the first letter is commonly accepted by the js community as a way to tell which objects are constructors. I know with require.js, this isn’t really necessary, since you can still refer to the returned object with a capitalized variable when passed into another module, but it’s a good practice.


gipoco.com is neither affiliated with the authors of this page nor responsible for its contents. This is a safe-cache copy of the original web site.