7.4. GateIn Module Definition

Warning

You are looking at documentation for an older release. Not what you want? See the current release documentation.

As you understood the module pattern explained in previous sections, now you are convinced to stick with it when developing in eXo Platform. The Platform is built on top of GateIn that introduces the GMD as a standard for writing and packaging JavaScript modules as portal resources.

The counter example as a portlet

For easily starting with GMD (GateIn Module Definition), you will turn the single html page in "counter" example into a portlet.

Note

Please get the source code at eXo Samples Repository.

Though you still can write inline scripts into your portlet JSP, in this example you turn all into modules, so the first thing is to re-write my.js. In the single html example it was:

require.config({
	baseUrl: "js",
	paths: {
		jquery: "jquery-1.7.1",
		util: "util"
	}
});

function myClick(){
	require(["util", "jquery"], function(util, $){
		$("#result").text(util.count());
	});
}

The require.config() will be replaced by GMD configuration (later in gatein-resources.xml). Wrap any other code in a closure:

(function(util, $){
	$(document).ready(function(){
		$("body").on("click", ".counter-portlet button", function(){
			$("#result").text(util.count());
		});
	});
})(util, jq);

It changes much because you no longer write a global named function (myClick) and attach it to a button directly in HTML. Instead you use jQuery and a CSS selector. The selector should point to the right HTML element that you write in your portlet template, in this example it is counter.jsp:

<div class='counter-portlet'>
<h2>The Counter Portlet</h2>
<p>You've clicked <span id="result">0</span> times.</p>
<button>Click me</button>
</div>

The last thing is to declare your GMD modules. It is done in gatein-resources.xml:


<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">
    <module>
        <name>util</name>
        <script>
            <path>/js/util.js</path>
        </script>
    </module>
    <portlet>
        <name>Counter</name>
        <module>
            <script>
                <path>/js/my.js</path>
            </script>
            <depends>
                <module>jquery</module>
                <as>jq</as>
            </depends>
            <depends>
                <module>util</module>
            </depends>
        </module>
    </portlet>
</gatein-resources>

The both dependencies - jQuery and util - are AMD modules. Simply declare "util" as a shared module. There is already a shared module named "jquery", and its version does not matter for now, so you use it without packaging a jQuery file. You do not need to package require.js too.

Now you can build, deploy and test the counter portlet before you look deeper into GMD.

Understanding GMD

So what happens to your modules then?

First, your js files are treated as GateIn resources, that means GateIn manages their lifecycle. They are deployed to the server, thus they are available all the time, but only loaded in the pages that use them.

To a JS resource, basically GateIn tweaks it into AMD modules, deploys it then loads it in the right pages. Here you see what happens to the my.js module:

  1. The code is wrapped in an AMD define():

    define('PORTLET/counter-portlet/Counter', ["SHARED/jquery","SHARED/util"], function(jq,util) {
    	var require = eXo.require, requirejs = eXo.require,define = eXo.define;
    	eXo.define.names=["jq","util"];
    	eXo.define.deps=[jq,util];
    	return (function(util, $){
    		$(document).ready(function(){
    			$("body").on("click", ".counter-portlet button", function(){
    				$("#result").text(util.count());
    			});
    		});
    	})(util, jq);
    });

    It is a normal AMD named module. The AMD name is formed with scope (PORTLET), the name of the app where the resource is registered (counter-portlet) and the module name configured in gatein-resources.xml (Counter).

  2. The module then is minified and deployed as a web resource that can be accessed by a URL like this:

    http://localhost:8080/portal/scripts/4.3.0/PORTLET/counter-portlet:COUNTER-min.js

    It mimics the following RequireJS paths configuration:

    baseUrl: "http://localhost:8080",
    paths: {
    	"PORTLET/counter-portlet/Counter": "/portal/scripts/4.3.0/PORTLET/counter-portlet:COUNTER-min"
    }

    The minified version is the one that takes effect, but you can view the unminified version by eliminating the "-min" part in the URL.

  3. Finally in the pages that contain your portlet, the modules (name and path) are added to the "require" object. This is a page object defined by eXo.

    
    <html>
    <head>
    <script type="text/javascript">
        var require = {
            "shim":     {...},
            "paths":    {...
                "SHARED/util": "/portal/scripts/4.3.0/SHARED/util-min",
                "PORTLET/counter-portlet/Counter": "/portal/scripts/4.3.0/PORTLET/counter-portlet:Counter-min",
            ...}
        };
    </script>
    </head>
    </html>

GMD and Non-AMD libraries

Now assume the library util is not compatible with AMD. For example, it is the following plain old JavaScript:

var counter = 0;
var count = function(){
	return (++counter);
}

You will use the adapter script in gatein-resources.xml configuration to make its AMD compatible:


    <module>
        <name>util</name>
        <script>
            <adapter>
                (function() {
                    <include>/js/util.js</include>
                    return {
                        count: count
                    };
                })();
            </adapter>
        </script>
    </module>

This adapter code wraps the original script (pay attention to include tag) in a closure, and returns the function count() that will be accessed by util.count(). The final code that is loaded in the page will be:

define('SHARED/util', [], function() {
	var require = eXo.require, requirejs = eXo.require,define = eXo.define;
	eXo.define.names=[];
	eXo.define.deps=[];
	return (function() {
		var counter = 0;
		var count = function(){
			return (++counter);
		}
		return {
			count: count
		};
	})();
});
Copyright ©. All rights reserved. eXo Platform SAS
blog comments powered byDisqus