ssossossosso
Documentation Home »The Oro Book »Layout »JavaScript Modularity
current version

JavaScript Modularity

Overview

OroPlatform uses Asynchronous Module Definition in order to provide JavaScript modularity.

AMD (Asynchronous Module Definition) – is a concept of creating modular JavaScript code with a defined set of dependencies. It defines the order in which resources have to be loaded and executed and therefore keeping the global scope clean.

RequireJS

OroPlatform leverages the RequireJS library to follow the AMD approach. RequireJS is a JavaScript library that provides functions to define modules and to declare dependencies on other modules in a module. A module is like a common JavaScript except that it defines a well-scoped object and hence does not pollute the global namespace. To use functions from other modules, the developers uses the RequireJS syntax to list the dependencies a module requires. Instead of have the need to manually pull the required dependencies into the module, RequireJS ensures that all dependencies are injected into the module following the inversion of control principle while making it possible to load dependencies asynchronously.

See also

You can find more information on how to write modular JavaScript using AMD in:

RequireJS comes with two important functions that form the backbone of the library:

define
Facilitates a module definition.
require
Handles the loading of dependencies in top-level JavaScript files.

Defining a Module

Each module is defined in its own file using define() which has the following signature:

1
2
3
4
5
define(
    module_id /*optional*/,
    [dependencies] /*optional*/,
    definition function /*function for instantiating the module or object*/
);

In its simplest form, a RequireJS module is just an object defining pair-value pairs:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// foo.js

define({
    foo: bar,
    foobar: baz,
    baz: function (param) {
        // do something

        return ...;
    }
});

You can even use a function if you need to do some initialization stuff:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// foo.js

define(function () {
    var bar = ...;

    return {
        foo: bar,
        foobar: baz,
        baz: function (param) {
            // do something

            return ...;
        }
    }
});

Note

Usually, you don’t have to define a module_id since it is automatically derived from the path of the file the module is stored in by the RequireJS optimization tool. In the example above, the module name would be foo as it was stored in the foo.js file.

Usually, your modules will need to work with some code from other modules. For example, a bar module depends on the previously created foo module:

1
2
3
4
5
6
7
// bar.js
define(['foo'], function (foo) {
    var baz = ...;
    var bar = foo.baz(baz);

    return bar;
});

In this example, the list of dependencies specified in the first argument, is resolved by RequireJS and the resolved modules are then passed as arguments to module function. This way, the baz function defined in the foo module can be called by invoking baz on the foo variable which actually holds the foo module object.

Loading Dependencies with require

Sometimes, you don’t have to define a module, but you need to pull in some dependencies and use them immediately. For example, your application may require both the foo and the bar module to boot:

1
2
3
require(['app', 'foo', 'bar'], function (app, foo, bar) {
    app.start(foo.baz(bar));
});

The usage of require() almost looks the same as define(), but there are some important difference to note:

  • require() does not build a module. Thus, you can’t specify a module id and nothing will be exported.
  • The last argument for require() always is a function that will be executed when all dependencies have been loaded. Contrary, the last argument passed to define() can be an object if you don’t have to execute any initialization logic. In define(), you cannot omit the last argument whereas you don’t need it in require at all and simply use it to load the application dependencies, for example.

Using RequireJS with OroPlatform

The RequireJSBundle eases the RequireJS integration into an application based on the Oro Platform. It scans each bundle for a RequireJS configuration file named requirejs.yml located in its Resources/config directory.

Such a configuration file can define two sections:

config
Configure modules and paths.
build
Customize the build process.

See also

You can find detailed information about the RequireJS configuration in the reference section.

RequireJSBundle was developed to simplify RequireJS configuration and building process. It collects parts of RequireJS configuration Resources/config/requirejs.yml from the bundles and merges them into a single config file.

Configuration

shim

Use the shim option to configure exports and dependencies for JavaScript libraries that don’t support RequireJS, but are loaded in the traditional way. For example, the following configuration defines modules named underscore (for the Underscore.js library) and backbone for the Backbone.js library:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# src/Acme/DemoBundle/Resources/config/requires.yml
config:
    shim:
        'underscore':
            exports: '_'
        'backbone':
            deps:
                - 'underscore'
                - 'jquery'
            exports: 'Backbone'

The deps option is used to define the list of dependencies (the Backbone.js library requires the Underscore.js and the jQuery libraries). The exports option specifies which object will be exposed by the module.

Note

Use the paths option to configure the paths where the library files can be located.

map

Sometimes, you may want to load a different version of a module based on the context it is required. For example, the OroUIBundle ships with an extended version of the jQuery library. Use the map option to substitute a module ID for a given prefix:

1
2
3
4
5
6
7
# src/Acme/DemoBundle/Resources/config/requires.yml
config:
    map:
        '*':
            'jquery': 'oroui/js/jquery-extend'
        'oroui/js/jquery-extend':
            'jquery': 'jquery'

The example uses the special * which maps all module prefixes. This means that all modules get the extended jQuery library from the OroUIBundle. However, since the bundle itself needs the original version of the library to be able to extend it, it will receive the original version given that there is the more specific oroui/js/jquery-extend entry that will take precedence.

paths

The paths option tells the optimization tool under which locations certain modules can be found:

1
2
3
4
5
6
7
# src/Acme/DemoBundle/Resources/config/requires.yml
config:
    paths:
        'jquery': 'bundles/oroui/lib/jquery-1.10.2.js'
        'underscore': 'bundles/oroui/lib/underscore.js'
        'backbone': 'bundles/oroui/lib/backbone.js'
        'oroui/js/jquery-extend': 'bundles/oroui/js/jquery-extend.js'

Build Process Customization

You can use the build option to exclude a module from being included in the build file by the optimization tool:

1
2
3
4
# src/Acme/DemoBundle/Resources/config/requirejs.yml
build:
    paths:
        'bootstrap': 'empty:'

With this configuration, the bootstrap module will be loaded from its actual path on runtime.

Full Configuration Example

A full working example of a RequireJS configuration in a bundle can look like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# src/Acme/DemoBundle/Resources/config/requirejs.yml
config:
    shim:
        # shim configures the exports and dependencies for older, traditional
        # "browser globals" scripts that do not use define() to declare
        # the dependencies and set a module value;
        'jquery-ui':
            deps:
                - 'jquery'
        'underscore':
            exports: '_'
        'backbone':
            deps:
                - 'underscore'
                - 'jquery'
            exports: 'Backbone'
    map:
        # maps for the given module prefix, instead of loading the module with
        # the given ID, substitutes a different module_id;
        '*':
            'jquery': 'oroui/js/jquery-extend'
        'oroui/js/jquery-extend':
            'jquery': 'jquery'
    paths:
        # path mappings for module names not found directly under baseUrl
        'jquery': 'bundles/oroui/lib/jquery-1.10.2.js'
        'jquery-ui': 'bundles/oroui/lib/jquery-ui.min.js'
        'bootstrap': 'bundles/oroui/lib/bootstrap.min.js'
        'underscore': 'bundles/oroui/lib/underscore.js'
        'backbone': 'bundles/oroui/lib/backbone.js'
        'oroui/js/jquery-extend': 'bundles/oroui/js/jquery-extend.js'

build:
    paths:
        # says not to include bootstrap module into the build file
        'bootstrap': 'empty:'
Browse maintained versions:
current1.102.02.3