Building Modular Web Applications with Backbone.js and RequireJS — Sample App

directory

In recent months, I have been sharing different versions of the Employee Directory sample application built with different technology stacks, different frameworks, and different back-end (REST services) implementations.

A number of you have asked for a modular version of the application built using RequireJS. So here it is.

RequireJS

If you are already familiar with RequireJS and just want to dive into the Backbone.js/RequireJS sample, you can safely skip this section.

A complete discussion of the benefits of Asynchronous Module Definition (AMD) and RequireJS is beyond the scope of this article, but a quick look at index.html in a recent version of the employee directory app highlights one of the problems when you don’t use a script loader like RequireJS:

<script src="lib/jquery-1.9.1.min.js"></script>
<script src="lib/underscore-min.js"></script>
<script src="lib/backbone-min.js"></script>
<script src="bootstrap/js/bootstrap.js"></script>
<script src="js/app.js"></script>
<script src="js/views/shell.js"></script>
<script src="js/views/home.js"></script>
<script src="js/views/contact.js"></script>
<script src="js/views/employeelist.js"></script>
<script src="js/views/employeedetails.js"></script>
<script src="js/models/model-in-memory.js"></script>

While small, the Employee Directory sample application imports eleven scripts. Imagine what a large application would look like. Of course, you can (and should) use a build process to concatenate and minify these scripts before deploying your application. But that doesn’t solve all the problems: In what order should these scripts be loaded to make sure all dependencies can be resolved? If this application is built by a team of developers, what’s the process for managing the list of dependencies between scripts/modules and make sure they can still be resolved as new modules are added by members of the team? How do you write code that actually minimizes hard dependencies between modules?

RequireJS is a script and module loader that addresses these issues:

  • When you run the application during development, it will load modules from separate files as needed and automatically resolve dependencies. Before deploying the application, you can run the RequireJS optimizer to combine scripts and minify them using UglifyJS (or the Closure Compiler).
  • It provides an implementation of the module pattern.
  • It manages dependencies between modules, ensuring a module is loaded before it is used by other modules.
  • It allows you to centrally manage dependency mappings (using a map config) so you can easily swap the version of a module that is injected in other modules. For example, you could choose to inject a mock dataprovider for testing, and later replace it with the actual JSON dataprovider for deployment. See the “Map Config” section below for an example.

There are other ways to achieve these goals. A discussion of the pros and cons of AMD compared to other options is beyond the scope of this article. The goal of this post is simply to share a practical example of a modular application built with Backbone.js and RequireJS.

The Sample Application

Click here to run the Backbone.js/RequireJS version of the application.

Below are a few simplified examples of the modularized Backbone.js components used in the application. Check out the source code of the application on GitHub for the complete implementation.

Modularized Model

define(function (require) {

    "use strict";

    var $           = require('jquery'),
        Backbone    = require('backbone'),

        Employee = Backbone.Model.extend({

            urlRoot: "http://localhost:3000/employees",

            initialize: function () {
                this.reports = new EmployeeCollection();
                this.reports.url = this.urlRoot + "/" + this.id + "/reports";
            }

        }),

        EmployeeCollection = Backbone.Collection.extend({

            model: Employee,

            url: "http://localhost:3000/employees"

        });

    return {
        Employee: Employee,
        EmployeeCollection: EmployeeCollection
    };

});
This application uses the syntactic sugar described here. This version of the define() function is less error-prone, and easier to read and to manage.

Modularized View

define(function (require) {

    "use strict";

    var $           = require('jquery'),
        _           = require('underscore'),
        Backbone    = require('backbone'),
        tpl         = require('text!tpl/Employee.html'),

        template = _.template(tpl);

    return Backbone.View.extend({

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

    });

});

Modularized Router

define(function (require) {

    "use strict";

    var $           = require('jquery'),
        Backbone    = require('backbone'),
        
        $content = $("#content");

    return Backbone.Router.extend({

        routes: {
            "":                 "home",
            "employees/:id":    "employee"
        },

        home: function () {
            require(["app/views/Home"], function (HomeView) {
                var view = new HomeView({el: $content});
                view.render();
            });
        },

        employee: function (id) {
            require(["app/views/Employee", "app/models"], function (EmployeeView, models) {
                var employee = new models.Employee({id: id});
                employee.fetch({
                    success: function (data) {
                        var view = new EmployeeView({model: data, el: $content});
                        view.render();
                    }
                });
            });
        }

    });

});

Loading Templates with the Text Plugin

It is a lot easier to author your HTML templates in separate files. The RequireJS text plugin makes it easy to load these templates as dependencies.

var tpl = require('text!tpl/Employee.html');

Map Config

RequireJS allows you to centrally manage dependency mappings (using a map config) so you can easily replace the version of a module that is injected in other modules. In the map configuration below, I mapped the ‘app/models/employee’ dependency to the ‘app/models/memory/employee’ module which is a simple in-memory data provider. You can easily replace the in-memory data with actual JSON services throughout the application by replacing ‘app/models/memory/employee’ with ‘app/models/json/employee’ in the mapping below defined in app.js.

require.config({

    baseUrl: 'js/lib',

    paths: {
        app: '../app',
        tpl: '../tpl'
    },

    map: {
        '*': {
            'app/models/employee': 'app/models/memory/employee'
        }
    },

    shim: {
        'backbone': {
            deps: ['underscore', 'jquery'],
            exports: 'Backbone'
        },
        'underscore': {
            exports: '_'
        }
    }
});

Source Code

The source code for this application is available in this GitHub repository.

Because the modules of this application are loaded using XMLHTTPRequest, you will get a cross domain error (Access-Control-Allow-Origin) if you load index.html from the file system (with the file:// protocol). Make sure you load the application from a web server. For example: http://localhost/directory-backbone-bootstrap-require.

If you want to go beyond the in-memory datastore, you can download the REST services in the following repositories:

directory-rest-nodejs (Node.js/MongoDB implementation)
directory-rest-php (PHP implementation)
directory-rest-java (Java implementation coming soon)

41 Responses to Building Modular Web Applications with Backbone.js and RequireJS — Sample App

  1. Roland June 28, 2013 at 7:10 am #

    Great! Thank you so much for that gem tutorial!

  2. Aaron July 7, 2013 at 3:33 pm #

    Thanks for the post – i’ve had trouble with slowness at startup when trying to use require with phonegap but this example is quite snappy.

    One question – the navigation between the employee list and detail screens is done with regular anchor tags. After integrating into phonegap, I’m noticing that there is a short lag after tapping one of these elements and the page transitioning – presumably due to “click” vs “touch” on the phone. What’s the best way to make that more responsive?

  3. Noah July 8, 2013 at 9:45 am #

    Thanks for this tutorial Christophe. Just a query. How would I call RESTful APIs in this modular structure? Would I use .ajax ?

    • Christophe Coenraets July 8, 2013 at 10:34 pm #

      Since this is a Backbone.js application, calls to the RESTful API will be handled automatically by the models. In general, the fact that an application is modular doesn’t change the way you call your RESTful services.

      • Viswanathan July 25, 2013 at 10:16 am #

        please update restful web service using java and also db insert query.

  4. Helga B July 25, 2013 at 10:09 am #

    Thank you for this informative sample.

    • Viswanathan July 25, 2013 at 10:20 am #

      I want run this app using android+phonegap with localhost java base restful service can you please update the project with all this. really your are doing great job.

      Thanks a Lot :-)

  5. frye boots sale August 17, 2013 at 3:32 am #

    Thank you for this informative sample

  6. Web application development services August 20, 2013 at 6:51 am #

    “RequireJS allows you to centrally manage dependency mappings (using a map config) so you can easily replace the version of a module that is injected in other modules.” This is a great possibility but I have a question. I’m working on web application development servicesand would like to know, the map fits under all of these projects?

  7. Muhaimin August 26, 2013 at 4:42 am #

    hi

    is there anyway you could make an example combining the google map. I have a difficulty to load it with backbone

  8. Amreen August 27, 2013 at 5:27 am #

    Thanks for your helpful information.

  9. Muhaimin August 27, 2013 at 5:42 am #

    Hi chris

    i try to follow your example by combine the google map. I dont really understand / familiar with your programming style. Hope you can enlighten my by form it on my github

    https://github.com/muhaimincs/gmap_backbone

    thanks chris

  10. Jason September 3, 2013 at 12:39 am #

    Hi Chris,

    Thanks for the wonderful tutorial. I’m facing a problem here running the programme that I hope you can help me with. I’m using your Node.js/MongoDB implementation to run the app from localhost:3000, but I ended up with an empty homepage i.e. the is not rendered. If I request for localhost:3000/employees/:id for example, I get the JSON object of that employee, so I believe that the server is able to retrieve the data from the database.

    I just can’t seem to get why I have an empty … It seems that there must be something wrong with the Backbone views rendering…

    Would appreciate any help!

    • Jason September 3, 2013 at 3:10 am #

      Sorry, I forgot to use the tag. I wanted to say I get an empty “body”

      • Longchi March 4, 2014 at 7:03 am #

        Hi Jason,

        Did you ever get a response to this answer? I am having the same problem.

        Thanks

  11. metal buildings houston September 13, 2013 at 9:33 pm #

    Having read this I believed it was very enlightening.
    I appreciate you taking the time and energy to put this short article together.
    I once again find myself spending a significant amount of time both reading and leaving comments.
    But so what, it was still worth it!

  12. Razvan September 26, 2013 at 7:05 am #

    Hi! I noticed that if I add a button in the employee template:
    Save
    And in the view I add:
    events : {
    “click #save” : “saveInfo”
    },

    and
    saveInfo: function() {
    console.log(“enter”);
    }

    If I walk a bit through the app, everytime I enter employee page, and press the button, the number of prints increases:
    The first time I enter the page I click save -> enter
    After this, I go to home page, I go back to employee, hit the button save -> enter\n enter
    and so on

  13. Todd October 29, 2013 at 2:02 am #

    The directory-rest-php library doesn’t seem to work with this example. If i remove the reports section from the employee.html tpl file then it works … seems the php service isn’t returning reports correctly (what is this reportscount as opposed to just reports being returned by the memory service?).

    Removing the code from the tpl below makes the rest of the app function… how do i get the reports in there?

    0) { %>
    <a href="#employees//reports”>View Direct Reports

  14. Max November 19, 2013 at 3:37 pm #

    Nice article!!

    But, first of all i work on linux maschines so i had to rename all files spelled bad, because my webserver can not find them and require.js can not resolve the dependencys.

  15. Jonathan Gravois January 31, 2014 at 12:26 pm #

    Any thoughts on implementing this in SharePoint 2013 Foundation?

    We are in need of an Employee Directory for our Intranet and I have yet to see one as good-looking or complete as yours so I want to try to build this on the SharePoint platform. Since SP2013 is all about using JavaScript rather than XSLT, I think it is possible.

    I am just wondering if you would suggest a specific framework (you’ve done this in Angular and BackBone, at least, maybe more) or if you have any other thoughts that may help me.

  16. Erik Ringsmuth February 19, 2014 at 3:36 pm #

    I have a new solution for routing AMD modules.

    RequireJS Router https://github.com/erikringsmuth/requirejs-router

    This takes the approach of lazy loading AMD modules as you navigate to each page. With the Backbone router you need to require all of your views as dependencies up front. This loads all of your apps Javascript on the first page load. The RequireJS Router lazy loads modules as you navigate to each route.

    Example main.js used to run your app


    define([], function() {
    'use strict';

    // Configure require.js paths and shims
    require.config({
    paths: {
    'text': 'bower_components/requirejs-text/text',
    'router': 'bower_components/requirejs-router/router'
    }
    });

    // Load the router and your layout
    require(['router', 'js/layout/layoutView'], function(router, LayoutView) {
    var layoutView = new LayoutView();
    // The layout's render method should draw the header, footer, and an empty main-content section
    // then load the content section.
    // render: function() {
    // this.$el.html(this.template({model: this.model}));
    // router.loadCurrentRoute();
    // }

    // Configure the router
    router
    .registerRoutes({
    home: {path: '/', moduleId: 'home/homeView'},
    order: {path: '/order', moduleId: 'order/orderView'},
    notFound: {path: '*', moduleId: 'notFound/notFoundView'}
    })
    .on('statechange', function() {
    // Render the layout before loading the current route's module
    layoutView.render.call(layoutView);
    })
    .on('routeload', function(module, routeArguments) {
    // Attach the content view to the layoutView's main-content section
    layoutView.$('#main-content').replaceWith(new module(routeArguments).render().el);
    })
    .init({
    // We're manually calling loadCurrentRoute() from layoutView.render()
    loadCurrentRouteOnStateChange: false
    });
    );
    );

  17. noah full movie online April 26, 2014 at 1:32 am #

    It was not global over the entire earth, but it certainly was global if
    you were in Noahs shoes. That’s because the Bible is still the “Greatest Book Ever Written and the Greatest Story Ever Told. If we do this, more trees will be spared and less toxic greenhouse gases will be emitted into the atmosphere.

  18. hollywood video May 2, 2014 at 3:12 am #

    Heya! I understand this is sort of off-topic however I needed to
    ask. Does operating a well-established website such as yours require a lot
    of work? I am brand new to operating a blog however I do write in my diary on a daily
    basis. I’d like to start a blog so I can easily share my own experience and feelings online.

    Please let me know if you have any kind of suggestions or tips
    for new aspiring bloggers. Thankyou!

  19. Sivanthi May 5, 2014 at 4:49 am #

    Thanks for nice tutorial. continue your writing………..

  20. Joeann June 9, 2014 at 9:28 pm #

    Ahaa, its good dialogue concerning this piece of writing here at this blog, I have
    read all that, so at this time me also commenting at
    this place.

  21. panele fotowoltaiczne cennik June 10, 2014 at 7:43 am #

    My partner and I stumbled over here coming from a different web page
    and thought I should check things out. I like what I see
    so now i am following you. Look forward to looking
    over your web page repeatedly.

  22. pine bedroom sets June 10, 2014 at 3:48 pm #

    The actually ideal method to uncover qualified and correctly educated professionals
    to your cooking area storage organization is by posting adverts
    on-line collectively with by the use of other media
    to get outstanding ultimate final results.
    No, the measurements are taken by the experts who visit your home after
    you place a request over the phone. This is important because you never know what may go wrong
    with the modular components in the future.

  23. drzwi wewnętrzne białystok June 20, 2014 at 5:46 am #

    Helpful info. Fortunate me I discovered your website accidentally, and I am
    surprised why this twist of fate didn’t took place
    earlier! I bookmarked it.

  24. play.google.com June 22, 2014 at 4:03 am #

    I am extremely inspired with your writing abilities and also with the layout on your
    weblog. Is this a paid subject or did you customize it yourself?
    Either way stay up the excellent high quality writing,
    it’s uncommon to see a great weblog like this one
    today..

  25. Carin July 7, 2014 at 1:09 pm #

    Today, World of Warcraft has reserved a special place in the hearts of MMO lovers.
    However, there are also some downsides to linking up with a large blogging site.
    When games are classified as MMO, massive comes into effect when forty or fifty
    people are in one room fighting for the glory of their team- multiplied by
    hundreds of other rooms can equal thousands of challengers competing at the same time.

  26. firma transport mutari bucuresti July 8, 2014 at 6:06 am #

    Thank you, I’ve just been looking for infoormation about this
    subject for a long time and yours is the bedst I’vecame upon so far.
    However, whatt about the bottom line? Aree you positive in regards to the source?

  27. Zombie Diary 2 cheats free July 10, 2014 at 5:16 am #

    A number of tools come as bonus with the application. Yesterday,
    along with the second DA letter, I got a mysterious letter that read that I would receive an Android Touchpad Tablet Computer from
    their “gifting department”. Advantages for hiring
    talented Android application developer or programmer.

  28. Winifred July 10, 2014 at 9:56 pm #

    Hello! Thiis post could not be written any better! Reading through this post reminds me of myy
    good old room mate! He alpways kept talking about this. I will forward
    this article to him. Fairly certain he will have a good read.

    Thanks for sharing!

  29. futuroscope July 12, 2014 at 2:59 am #

    What i do not understood is in fact how you are not really
    a lot more neatly-liked than you might be right now.

    You’re very intelligent. You already know therefore significantly relating to this topic, produced
    me in my view imagine it from so many various angles. Its like women and men are not involved except
    it’s something to do with Woman gaga! Your personal stuffs
    excellent. Always care for it up!

  30. Valencia July 16, 2014 at 11:07 am #

    Hello, every time i used to check blog posts here in the early hours in the morning,
    as i like to gain knowledge of more and more.

  31. best love quotes July 16, 2014 at 12:25 pm #

    Hi! This post couldn’t be written any better! Reading this post reminds me of my good old room mate!
    He always kept chatting about this. I will forward this write-up to him.
    Fairly certain he will have a good read. Thank you for sharing!

  32. boyfriend girlfriend quotes July 18, 2014 at 4:03 am #

    Simply wish to say your article is as amazing.
    The clarity in your post is simply nice and i can assume you’re an expert on this subject.
    Well with your permission let me to grab your RSS
    feed to keep updated with forthcoming post. Thanks a million and please keep
    up the rewarding work.

  33. erwin October 4, 2013 at 5:51 am #

    I reply to myself !!!
    I added a cors.js script to the server
    module.exports = function(req, res, next) {
    res.header(“Access-Control-Allow-Origin”, “*”);
    res.header(“Access-Control-Allow-Headers”, “X-Requested-With”);
    next();
    }

    and I added the following lines into server.js below var app = express();

    var app = express();

    cors = require(‘./cors’);
    app.use(cors);

    and it’s running fine now…

Trackbacks/Pingbacks

  1. Building Modular Mobile / PhoneGap Apps with Backbone.js, RequireJS and Topcoat | Christophe Coenraets - June 27, 2013

    [...] my previous post, I discussed the process of building modular applications with Backbone.js and RequireJS, and I [...]

  2. Sample Mobile Application with AngularJS | Christophe Coenraets - November 5, 2013

    [...] Employee Directory with Backbone.js, RequireJS, and Twitter Bootstrap [...]

Leave a Reply

css.php