Chapter 32. JavaScript Modularity

32.1. JavaScript Modules

The Red Hat JBoss Portal JavaScript handling improvements are built upon the concept of the JavaScript module. JavaScript does not provide native support for namespacing and so the notion of modules was designed to solve this problem. These modules provide namespacing and more; the module pattern allows developers to create dependencies between modules and resolve them at runtime, enabling on demand and parallel loading of JavaScript resources.
For more information see the RequireJS documentation at http://requirejs.org/. An in-depth article about JavaScript module patterns is available at http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth.

32.2. Introduction to Modules

A module can be thought of as:
  • An identifier or name;
  • A list of the module's dependencies;
  • Packaged code, usually expressed as a self-executing function.
  • The product, an object produced by the module that is usually consumed by other modules.
At runtime the dependency system defines a graph of functions to execute, the product of each module being injected in the other modules. It can be seen as a simple dependency injection system able to load modules in an asynchronous and parallel fashion, providing parallel loading, namespacing, and dependency management.
The portal provides support for different module formats:
GateIn Module Definition (GMD)
This format is the most appropriate way to define a JavaScript module in the portal. The module is expressed as a simple function, the list of dependencies being expressed in the gatein-resources.xml file. This format clearly decouples the module's code in a JavaScript file from the module's dependencies, expressed in a single XML file, and is the best compromise for integrating JavaScript in the portal.
Asynchronous Module Definition (AMD)
This is the native module format supported by RequireJS.
Adapted Module
This format is the most flexible as it adapts to other module formats such as CommonJS modules.
To explore the benefits of modules (beyond namespacing) the following example uses a module consuming the jQuery library:
(function($) {
  $("body").on("click", "mybutton", function(event) {
  alert("Button clicked");
  };
})($)
The JavaScript component of our module is a function that takes the jQuery $ object as an argument:
  • The jQuery object is provided as a module dependency expressed as a function argument $. The module code can use any name for jQuery; in our example we use the $ name.
    $ jQuery can be used without needing to use the $.ready function because jQuery will be already loaded and initialized when the function is executed.
  • The module is scoped to the entire page and this code will react to any click on the .mybutton selector. Since a single event listener handles as many portlets as are on the page, this benefits performance and simplicity.
To integrate this module, it is declared in the gatein-resources.xml file of the WAR archive:

<gatein-resources
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.gatein.org/xml/ns/gatein_resources_1_3 http://www.gatein.org/xml/ns/gatein_resources_1_3"
xmlns="http://www.gatein.org/xml/ns/gatein_resources_1_3">
  <portlet>
    <name>MyPortlet</name>
    <module>
      <script>
        <path>/mymodule.js</path>
      </script>
      <depends>
        <module>jquery</module>
        <as>$</as>
      </depends>
    </module>
  </portlet>
</<gatein-resources>
The module tag declares the module and its dependencies. In this case it is declared that jQuery is the only dependency and that it is named $ when the module is executed.
A dependency relationship between the module and the jQuery module has been created such that:
  • The module will be loaded only when the portlet is displayed;
  • The portal loads the jQuery module before loading our module;
  • Several different versions of jQuery can coexist in the browser without conflicting.
This introductory example outlines the important features provided by modules: performance, isolation, dependency management and ease of use.

32.3. Script Support

While modules are preferred whenever possible, they cannot be used in every instance. For example, when an existing portlet uses an inline script it will access the global namespace to find functions or objects. To accommodate these instances, the traditional method of using JavaScript with a simple script declaration in the head section of a web page continues to be supported.
Dependencies between scripts can be created to control the order of the scripts in the head section and resolve dependency problems as much as possible.