Skip to end of metadata
Go to start of metadata

Introduction

Resource Management, appeared since the release 3.3.0-GA of GateIn, has seen nice management of JavaScript resources on the server side. However, the feature contains a gap as it does not support a real asynchronous loading on client side, that encouraged us to integrate the concept of Asynchronous Module Definition into GateIn.

RequireJS was selected from the matrix of AMD-compliant libraries.

How RequireJS works?

Let alpha.js be a JavaScript file whose content is in module definition format.

alpha.js

Consider a web page using RequireJS to access square method from alpha.js

A JavaScript code block under <body> element requires alpha module, callback method passed to require asks the browser to display alpha.square(10) in an alert message. As the block is interpreted, there are two scenarios:

alpha.js has been loaded into browser

alpha object is created (if it does not exist) thanks to factory method.

Factory method

Once the alpha module is available, alpha.square(10) returns 10*10 = 100.

alpha.js has not been loaded into browser yet

RequireJS searches through require variable the file path associated with alpha, what it finds out is actually the path to alpha.js. Afterward, a <script> element loading alpha.js is auto generated under <head> and we are falling back to the first scenario.

Auto generated <script> element

RequireJS Integration

For the moment, integration work is converging to:

  • Define global require variable in the top <script> element that maps modules to physical JavaScript sources.
  • Ensure that RequireJS library is always loaded posterior to declaration of require variable.
  • Have each ON_LOAD JavaScript resource enclosed in a module definition block.
  • Amend existing JavaScript code so that it is accessible from AMD coding styles.

require variable

UIPortalApplication.gtmpl

require is computed dynamically in UIPortalApplication.gtmpl. The variable establishes a map from fully-qualified names of resources to their physical JavaScript source.


Load RequireJS library

RequireJS library is configured as script of bootstrap - an IMMEDIATE resource, so it is always loaded after declaration of require


Have ON_LOAD resource enclosed in module definition block

PORTLET/web/SiteMapPortlet resource

Before transmission to client's browser, content of the resource is converted into module definition format.

PORTLET/web/SiteMapPortlet module

The server side exploits information from resources graph while determine module id, dependencies array and arguments array of factory function.

Amend existing JavaScript code so that it is accessible from AMD coding styles

Given that UISiteMap.js is a file bundled in PORTLET/web/SiteMapPortlet resource, its entities are now published via _module object.

As a result, methods declared in UISiteMap are visible to callback function passed to require.

Using RequireJS Feature

This section deals with following topics

  • How to configure a new AMD module
  • How to use an AMD module
  • How to use JQuery plugin as an AMD module
  • Pre-defined AMD modules in GateIn

Module configuration

Creating a new AMD module is roughly the same as creating an ON_LOAD JavaScript resource. Each ON_LOAD resource is transmitted to client-side in form of an AMD module definition.

Take an example of math ON_LOAD resource.

The resource wraps a single JavaScript file simple_operator.js

simple_operator.js

Associated AMD module on client-side would be

math resource on client's browser

The id passed to module definition block is SHARED/math, this value is actually the fully-qualified name of math resource.

Fully-qualified name of a JavaScript resource is combination of simple name and a prefix specified by its scope:

  • SHARED/ - for SHARED scope resource.
  • PORTAL/ - for PORTAL scope resource.
  • PORTLET/portlet_app_name/ - for PORTLET scope resource.

simple_operator.js shows a special statement _module = SimpleOperator, such assignment grants SHARED/math module with access to add/subtract methods.

Module alias

RequireJS introduces <as> element, an extra configuration of ON_LOAD resource denoted as module alias. With this extended configuration, there is freedom in naming factory method 's arguments (see section JQuery Plugin for an use case where factory arguments must take predefined names).

In gatein-resources.xml, <as> appears as sibling of resource's <name> element or as child of resource's <depends> element.

Argument names of factory methods for SHARED/arithmetic and SHARED/calculus are customized as shown in below module definition blocks.

Module definition blocks for SHARED/arithmetic and SHARED/calculus

Dependencies

It has been mentioned earlier that while converting content of resource into module definition format, the server side extracts information from resource graph to generate array of dependency module ids. However, that array does not take into account indirect dependency resource.

Let A, B, C be three resources where A depends on B and B depends on C. Although C is a descendant of A in resource graph, definition of module SHARED/A has no reference to SHARE/C unless C is configured as direct dependency of A.

Wrong configuration

leads to incomplete module definition

SHARED/C is missing

Correct configuration

Module usage

Throughout this section, MathPortlet portlet would be used as demonstrating example. JavaScript resource of the portlet depends on math and jquery resources.

For an arbitrary portal page showing MathPortlet portlet window(s), its HTML source includes a <script> element:

require variable in web page showing MathPortlet

JavaScript API wrapped in PORTLET/portlet_app_name/MathPortlet, SHARED/math and SHARED/jquery modules (or any module generated via declarative configuration of GateIn), is accessible in two manners:

  • Using pure JavaScript code
  • Using GateIn API

Using pure JavaScript code

Below simple portlet view shows generic coding style interacting with PORTLET/portlet_app_name/MathPortlet, SHARED/math and SHARED/jquery modules. The main idea of is to put all business code in callback passed to require block.


Portlet view

Example's complexity is augmented with extra JavaScript code under PORTLET/portlet_app_name/MathPortlet

advance_operator.js delcares product method and publishes it to PORTLET/portlet_app_name/MathPortlet module object.

Updated portlet view.

Portlet view

Using GateIn API

RequireJS Integration provides a Java API that allows developers to interact with AMD modules (i.e modules constructed with declarative configuration) from Groovy template.

JavascriptManager and RequireJS

Expression jsManager.require("first", "firstAlias").require("second", "secondAlias").addScripts("BUSINESS CODE") in Groovy template would be interpreted by Groovy engine to a require block.

Suppose that MathPortlet was built over WebUI framework, then it could use following Groovy template to generate the same view.

Groovy template of MathPortlet

JQuery Plugin

  • How to configure JQuery plugin as AMD module.
  • How to interact with JQuery plugin in AMD coding style.

Configure JQuery plugin

Consider a simple JQuery plugin declared in simple-plugin.js

simple-plugin.js

Typical configuration to have this plugin appeared in form of AMD module definition would be:

As the core of example plugin is a self-execution block whose input is jQuery object, the argument in the factory method of SHARED/simplePlugin, mapping to SHARED/jquery module object, must be jQuery. <as>jQuery</as> saves us here!

"SHARED/simplePlugin module definition

Interact with JQuery plugin

Resource configuration of MathPortlet is amended to have simplePlugin in dependencies list.

Updated configuration for MathPortlet

Call to doesPluginWork method is added into portlet view

Portlet view

In spite of the fact that there is not direct reference to simplePlugin in callback, it is imperative to put SHARED/simplePlugin in dependencies array. With SHARED/simplePlugin in the array, it is certain that $ has been extended with plugin method prior to callback execution.

In practice, it is recommended that JQuery-plugin module ids are put at the end of dependencies array. That avoids unexpected wrong passing of input arguments to callback function.

Pre-defined Modules

Any ON_LOAD JavaScript resource in GateIn are now available as AMD modules.

Miscellaneous

Global variable

In AMD programming paradigm, use of global variables is not recommended.

Example of global variable
Labels:
None
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.