Tutorial RequireJS and PhoneGap

As a javascript project grows larger and larger, if you do not set up proper organization of your code, it will soon turn into a real nightmare (if it did not already). The Asynchronous Module Definition (AMD) pattern offers a good solution to this by allowing you to split your code into small and isolated modules, easier to test, maintain, optimize and extend.

The secret to building large apps is NEVER build large apps. Break up your applications into small pieces. Then, assemble those testable, bite-sized pieces into your big application. — Justin Meyer

In this tutorial we will see how to use RequireJS in your PhoneGap project. RequireJS is a JavaScript file and module loader, providing a powerful yet easy to use Asynchronous Module Definition API.

I included a few ideas and references from this interesting series of blog posts about javascript applications architecture by Aaron Hardy, which I strongly advise you to read!

Step 1 – Create the project

Let’s start by creating a sample project called “modular”.

$ phonegap create modular

And check that it works.

$ phonegap local build ios
$ phonegap run ios

Note: iOS or any other platforms will do. PhoneGap fails to run on some systems, in that case you can also open and run from XCode:
$ open platforms/ios/HelloWorld.xcodeproj

Step 2 – Setup

Now we are going to convert PhoneGap’s default project to use RequireJS.

First download RequireJS from here: http://requirejs.org/docs/download.html and copy the require.js file in the www/ folder.

We will then turn js/index.js into an AMD module. Let’s rename the file to js/app.js to reflect the feature it exports, then add the highlighted line on top and bottom of the file:

/* file: js/app.js */
define(function (require) {
    var app = {

        initialize: function() {
            this.bindEvents();
        },

        bindEvents: function() {
            document.addEventListener('deviceready', this.onDeviceReady, false);
        },

        onDeviceReady: function() {
            app.receivedEvent('deviceready');
        },

        receivedEvent: function(id) {
            var parentElement = document.getElementById(id);
            var listeningElement = parentElement.querySelector('.listening');
            var receivedElement = parentElement.querySelector('.received');

            listeningElement.setAttribute('style', 'display:none;');
            receivedElement.setAttribute('style', 'display:block;');

            console.log('Received Event: ' + id);
        }
    };
    return app;
});

This code makes use of RequireJS’ define() function. This function registers a new module, whose name is simply the name of the file without the extension, ie "app" in this case. We’ll see later for more “advanced” use of this function.

Finally, we proceed with some changes to index.html, that replaces the direct inclusion of js/index.js and call to app.initialize() with this:

<html>
...
<script type="text/javascript" src="phonegap.js"></script>
<script type="text/javascript" src="require.js"></script>
<script>
require.config({ baseUrl: "js" });
require(['app'], function (app) {
    app.initialize();
});
</script>
</body></html>

Line 4, we include the require.js library.

Line 6, we configure require to look for files within the “js” directory.

Line 7, we use call require(['app'], ...), to load the app module, then call its initialize() function as was done before.

The main benefit from this new approach is that “app” is no longer a global javascript variable, but a self contained module loaded on demand by RequireJS. No pollution of the global scope, no risk to accidentally use or override the “app” object from everywhere in your application.

If we build and run our application again, we should see the same result: the blinking “DEVICE IS READY”.

$ phonegap local build ios
$ phonegap run ios

Step 3 – More modules

Now let start making things interesting by adding more code to our project. I’ll simply start with earlier mentioned Aaron Hardy’s blog example, from which I borrowed some of the explanations too.

/* file: book.js */
define(function (require) {
    return {
        title: "PhoneGap Rocks",
        publisher: "Fovea"
    };
});

Add this file to www/js/. We now have a book module. As mentioned before, RequireJS assumes by default that the module name is the file path following the base url. In this case, that means that “book” is our assumed module name. When other code asks RequireJS for the “book” module, RequireJS will execute the module’s function (if it was not already executed), then pass to the caller whatever value is returned (in our case, it’s an object with title and publisher).

Now let’s make a “bookshelf” module in a new file named www/js/bookshelf.js and see how we can request the book module into it.

/* file: bookshelf.js */
define(function(require) {
	var book = require('book');
	return {
		listBook: function() {
			alert(book.title);
		}
	};
});

It’s pretty similar, we define a module as a function. Whatever object we return from this function will be registered with RequireJS as the bookshelf module. In our case, it’s an object with a listBook() method that alerts the book’s title.

Here, we also need access to the book module. When loading the “bookshelf” module, RequireJS will detect that it makes use of the require function to load “book”. If book.js hasn’t been loaded into the app yet, RequireJS will go load it. Once book.js is loaded and the book module is registered, RequireJS will execute our module function. We can safely access the book object we defined previously.

To finish things up, let’s add this module as a dependency to the “app” module and call bookshelf’s listBook function from receivedEvent. Solution is bellow, but you should already be able to do it without looking !

define(function (require) {
    var bookshelf = require('bookshelf');
    var app = {
[...]
        receivedEvent: function(id) {
            [...]
            bookshelf.listBook();
        }
    };
    return app;
});

Build and run, you should see an alert with the name of the book.

More

RequireJS can be used for different purpose and in different ways that the one presented in this tutorial. For the sake of simplicity I tried not to enter into too much details.

In a next tutorial, we will see how you can also use RequireJS to load external libraries and to compile all your project’s module into a single compressed javascript file.

I’m a consultant and developer for Mobile, Web, Games and Apps projects. I co-founded Fovea 8 years ago.

Tagged with: , ,
Posted in Blog
5 comments on “Tutorial RequireJS and PhoneGap
  1. Fazle says:

    were you able to test this on an actual iPhone?

  2. Fazle says:

    Trying to run on 3.3 looks like anything I do within require doesn’t seem to work. Probably the phonegap.js own declaration of require/define interfering?

    • Florin says:

      Maybe you need to shim the phonegap.js when configuring require. It should look something like below:

      require.config({
      paths: {
      jquery: ‘libs/jquery-latest.min’,
      bootstrap: ‘libs/bootstrap.min’,
      phonegap: ‘libs/phonegap’
      },

      // Sets the configuration for your third party scripts that are not AMD compatible
      shim: {
      ‘bootstrap’: {
      deps : [‘jquery’],
      exports : ‘Bootstrap’ //attaches “Bootstrap” to the window object
      },
      ‘phonegap’: {
      exports : ‘Phonegap’ //attaches “Phonegap” to the window object
      }
      } // end Shim Configuration
      });

      Also take a look at require js shim here: http://requirejs.org/docs/api.html#config-shim