4 Use RequireJS for Modular Development
About Oracle JET and RequireJS
RequireJS is a JavaScript file and module loader that simplifies managing library references and is designed to improve the speed and quality of your code.
RequireJS implements the Asynchronous Module Definition (AMD) API which provides a mechanism for asynchronously loading a module and its dependencies.
Oracle JET's modular organization enables app developers to load a subset of needed
features without having to execute require()
calls for each referenced
object. Each Oracle JET module represents one functional area of the toolkit, and it
typically defines more than one JavaScript object.
You do not have to use RequireJS to reference Oracle JET libraries, but it is required if you plan to use Oracle JET's internationalization or data visualization components in your app. The Oracle JET download includes the RequireJS library, and it is used by default in the Oracle JET Starter Templates and Cookbook examples.
For more information about RequireJS, see http://requirejs.org.
About Oracle JET Module Organization
The Oracle JET modules are listed in the following table with description and usage tips. Use this table to determine which modules you must load in your app. Where your app can directly interact with a module API, the available objects that the module returns also appear in the table. Your app would typically call functions on the returned object or instantiate new objects via the constructor function. For more information about module loading in Oracle JET apps, see API Reference for Oracle® JavaScript Extension Toolkit (Oracle JET) - JET Module Loading Overview.
Note:
Certain functionality that had been previously available from the now deprecated ojcore
, ojvalidation-base
, ojvalidation-datetime
, and ojvalidation-number
modules is provided by refactored modules that return their own object. The table indicates which modules have been refactored. You should explicitly import needed modules in the dependency list of your require function to use its classes.
Oracle JET Module | Refactored | Available Objects | Description | When to Use |
---|---|---|---|---|
|
No |
|
Classes of the JET Common Model |
Other than |
|
No |
|
Utilities for integrating Oracle JET's Common Model into Knockout.js |
Deprecated. Use the Oracle JET |
|
No |
Varies by component |
Oracle JET component modules. Examples include
Most Oracle JET components have their own module with the same name in lowercase and without hyphens as shown above, except for the following components:
|
Use component modules that correspond to any Oracle JET component in your app. |
|
No |
|
Oracle JET data binding for global attributes of any HTML element in the user interface |
Use when your app includes HTML elements (JET custom elements included) with databound global attributes (ones that use the : (colon) prefix) or that use the [[..]]/{{..}} syntax (for global attributes of custom elements). |
|
No |
|
Class for managing routing in single page apps |
Use if your single page app needs to manage routing. |
|
No |
|
Binding that implements navigation within a region of a single page app |
Use if your single page app needs to manage navigation within a page region. |
|
No |
|
Component that implements navigation within a region of a single page app |
Use if your single page app needs to manage navigation within a page region. |
|
No |
|
Used in conjunction with |
Use if your app adds animation effects. |
|
Yes, from |
|
Class that exposes the |
Use if your app needs to query the busy state of components on the page. |
|
Yes, from |
|
Class for setting and retrieving configuration options. |
Use if your app needs to set or retrieve app configuration details. |
|
Yes, from |
|
Utilities for setting up a logger and collecting logging information |
Use if your app needs to define logger callback functions. |
|
Yes, from |
|
Utilities for working with responsive screen widths and ranges. Often used in conjunction with |
Use if your app needs to work with responsive page design. |
|
Yes, from |
|
Utilities for getting theme information |
Use if your app needs to know about the theme's fonts or the target platform. |
|
Yes, from |
|
Utilities for time information |
Use if your app needs to calculate the position of a given time point within a range. |
|
Yes, from |
|
Service for retrieving translated resources |
Use if your app needs to work with translated resources. |
|
No |
|
Classes for managing attribute groups |
Use if your app needs to generate attribute values from data set values (key value pairs). |
|
No |
|
Utilities for converting Knockout templates to a renderer function that can be used in JET component renderer APIs |
Use if your app needs to work with Knockout templates. |
|
No |
|
Utilities for creating Knockout observables to implement responsive page design |
Use if your app needs to create observables for responsive page design. |
|
No |
|
Utilities for setting up and handling swipe to reveal on an offcanvas element |
Use if your app needs to support the swipe gesture. |
|
No |
|
Class for working with selection items in |
Use if your app needs to work with selections as a set. |
|
No |
|
Utilities for working with a JSON object to support the |
Use if your app creates an |
|
No |
|
Class for controlling off-canvas regions |
Use if your app needs to manage off-canvas regions. |
|
No |
|
Classes for aggregating data values in |
Use if your app renders aggregated cubic data in an |
|
No |
|
Data provider modules. Examples include:
|
Use if your app includes an Oracle JET component, and its data source is defined in
one of the |
|
No |
no public class available |
Time zone data |
Use if you want to add time zone support to |
|
Yes, from |
|
Color, date, and time conversion services. |
Use if your app needs to support conversion services. |
|
Yes, from |
|
Date and number validation services. |
Use if your app needs to support validation services. |
|
Yes, from |
|
Async date and number validation services. |
Use if your app needs to support async validation services. |
About RequireJS in an Oracle JET App
Oracle JET includes the RequireJS library and sample bootstrap file in the Oracle JET download.
The code below shows excerpts of the main-template.js
bootstrap file
distributed with the Oracle JET base distribution in the
appRootDir\node_modules\@oracle\oraclejet\dist\js\libs\oj\
directory.
Typically, you place the bootstrap file in your app's js
directory and rename
it to main.js
. The comments in the code describe the purpose of each
section. The sections that you normally edit are highlighted in bold.
/**
* Example of Require.js boostrap javascript
*/
(function () {
requirejs.config({
// Path mappings for the logical module names
paths: {
},
// This section configures the i18n plugin. It is merging the Oracle JET built-in translation
// resources with a custom translation file.
// Any resource file added, must be placed under a directory named "nls". You can use
// a path mapping or you can define a path that is relative to the location
// of this main.js file.
config: {
ojL10n: {
merge: {
// 'ojtranslations/nls/ojtranslations': 'resources/nls/myTranslations'
}
},
text: {
// Override for the requirejs text plugin XHR call for loading text
// resources on CORS configured servers
// eslint-disable-next-line no-unused-vars
useXhr: function (url, protocol, hostname, port) {
// Override function for determining if XHR should be used.
// content omitted for brevity
// Return true or false. true means "use xhr", false
// means "fetch the .js version of this resource".
return true;
}
}
}
});
}());
/**
* A top-level require call executed by the app.
* Although 'ojcore' and 'knockout' would be loaded in any case (they are specified as dependencies
* by the modules themselves), we are listing them explicitly to get the references to the 'oj' and 'ko'
* objects in the callback.
*
* For a listing of which JET component modules are required for each component, see the specific component
* demo pages in the JET cookbook.
*/
require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout', 'ojs/ojbutton', 'ojs/ojtoolbar', 'ojs/ojmenu'],
// add additional JET component modules as needed
// eslint-disable-next-line no-unused-vars
function (oj, ko, $) { // this callback gets executed when all required modules are loaded
// add any startup code that you want here
}
);
You can use RequireJS in a regular app as shown, or you can use RequireJS with
oj-module
element to define view templates and viewModels for page sections
in a single-page app. For example, the Oracle JET Starter Templates use the
oj-module
element with RequireJS to use a different view and viewModel when
the user clicks one of the navigation buttons.
For additional information about the Oracle JET Starter Templates, see About the Starter Templates. For more information about using ojModule
and templates, see Create Single-Page Apps.
Use RequireJS in an Oracle JET App
To use RequireJS in your app, edit the bootstrap file to add the Oracle JET modules you need. You can also add your own modules as needed for your app code.
If needed, install Oracle JET and install RequireJS at http://requirejs.org.
To use RequireJS in an Oracle JET app:
- In the bootstrap file or your app scripts, in the
require()
definition, add additional Oracle JET modules as needed. - Add any scripts that your app uses to the
require()
definition and update thefunction(ko)
definition to include the script. - Add any app startup code to the callback function.
- If your app includes resource bundles, enter the path to the bundle in the
merge
section.
Here's an example of the steps in order.
Your app uses the Oracle JET Common Model integrated with Knockout and includes anoj-dialog
. Add the highlighted modules to your bootstrap file or
app
script.require(['knockout','ojs/ojmodel'
,'ojs/ojknockout-model'
,'ojs/ojdialog'
], function(ko) // obtaining a reference to the oj namespace { } );
Then, to use a script named myapp.js
, add the highlighted code
to your require()
definition.
require(['myapp',
'knockout', 'ojs/ojmodel', 'ojs/ojknockout-model', 'ojs/ojdialog'], function(myapp
, ko) // obtaining a reference to the oj namespace { } );
Next, you have a Knockout binding call for an element named
dialogWrapper
. Add that to the callback
function.
require(['myapp', 'knockout', 'ojs/ojmodel', 'ojs/ojknockout-model', 'ojs/ojdialog'], function(myapp, ko) // obtaining a reference to the oj namespace {ko.applyBindings(new app()/*View Model instance*/,
document.getElementById('dialogWrapper'));
} );
Finally, you have a translations bundle, which you add to the
merge
section.
config: {
ojL10n: {
merge: {
'ojtranslations/nls/ojtranslations': 'resources/nls/myTranslations'
}
}
}
For more information about module loading in Oracle JET apps, see API Reference for Oracle® JavaScript Extension Toolkit (Oracle JET) - JET Module Loading Overview.
Add Third-Party Tools or Libraries to Your Oracle JET App
You can add third-party tools or libraries to your Oracle JET app. The steps to do this vary depending on the method you used to create your app.
If you used the Oracle JET command-line tooling to scaffold your app, you install the
library and make modifications to appRootDir/src/js/path_mapping.json
. If you created your app using any other
method and are using RequireJS, you add the library to your app and update the RequireJS
bootstrap file, typically main.js
.
Note:
This process is provided as a convenience for Oracle JET developers. Oracle JET does not support the additional tools or libraries and cannot guarantee that they will work correctly with other Oracle JET components or toolkit features.
To add a third-party tool or library to your Oracle JET app, complete one of the following procedures.
-
If you created your app with command-line tooling, perform the following steps:
-
In your app's root directory, enter the following command in a terminal window to install the library using NPM:
npm install library-name --save
-
Add the new library to the path mapping configuration file.
-
Open
appRootDir/src/js/path_mapping.json
for editing.A portion of the file is shown below.
{ "baseUrl": "js", "use": "local", "cdns": { "jet": "https://static.oracle.com/cdn/jet/17.1.0/default/js", "css": "https://static.oracle.com/cdn/jet/17.1.0/default/css", "config": "bundles-config.js" }, "3rdparty": "https://static.oracle.com/cdn/jet/17.1.0/3rdparty" }, "libs": { "knockout": { ... }, ...
Oracle JET's CLI helps manage third-party libraries for your app by adding entries to its local
path_mapping.json
file.For each library listed in the
libs
map in thepath_mapping.json
file, the following two actions happen at build time. First, one or more files are copied from somewhere (usuallyappRootDir/node_modules
) into theappRootDir/web
output folder. Second, arequireJS
path value is created for you to represent the path to the library; it is injected into the builtmain.js
file or optimized bundle. If you use TypeScript, then this should be the same path name that you use at design time for imports from the library.The following example is an existing path mapping entry for thepersist
library, which we analyze below to describe what each attribute does and how it relates to the two build-time actions defined above.1 | "persist": { 2 | "cdn": "3rdparty", 3 | "cwd": "node_modules/@oracle/oraclejet/dist/js/libs/persist", 4 | "debug": { 5 | "cwd": "debug", 6 | "src": [ 7 | "**" 8 | ], 9 | "path": "libs/persist/debug", 10 | "cdnPath": "persist/debug" 11 | }, 12 | "release": { 13 | "cwd": "min", 14 | "src": [ 15 | "**" 16 | ], 17 | "path": "libs/persist/min", 18 | "cdnPath": "persist/min" 19 | } 20 | },
-
Line 1: This is the name of the
libs
map entry ("persist
", in this case). This name is used as the name of therequireJS
path entry that is created; therefore, it is also the import path. The same name is used for the folder that gets created under theappRootDir/web/js/libs
folder in the built application.Note:
If your app uses TypeScript, then you'll also need atsconfig.json
filepaths
entry with the same name. - Line 2 (Optional): The
cdn
attribute sets the name of the CDN root for the location of this library. In this case, it is set to the value3rdparty
, which maps to a particular location in the Oracle CDN. Any third-party libraries that you add yourself (that is, those that are not present on the Oracle CDN) should not have acdn
orcdnPath
value at all, unless you maintain your own CDN infrastructure where you can place the library. - Line 3: The first role of each path mapping entry at build time is to copy files.
The
cwd
attribute on this line tells the tooling where to start copying from, and subsequent paths are relative to this root. This is generally some folder underappRootDir/node_modules/<library>
. - Lines 4 through 12: The
debug
andrelease
sections here are the same, with the exception that if you do a normal build, then the specifications in thedebug
section are followed, and if you do a--release
build, then the specifications in therelease
section are followed. For example, the latter case can allow the copying of only optimized assets for release. - Line 5 (Optional): In the context of the debug or release build, this second
cwd
attribute further refines the copy root for everything that follows. For example, when running a normal (debug) build for thepersist
library, the copy root isappRootDir/node_modules/@oracle/oraclejet/dist/js/libs/persist/debug
, where the debugcwd
value is appended to the top-levelcwd
value. Additionally, this folder name is used in the output; the copied files end up in theappRootDir/web/js/libs/persist/debug
directory. This attribute is optional; if omitted, then all copies are copied from paths relative to the rootcwd
location and placed in the root of the destination folder. - Line 6: The
src
attribute holds an array of all the files that you want to copy from your root location into the built app (that is, all the files that are actually needed at runtime). The paths you add here can either be specific file names, or you can use globs to match multiple files at once. In the example above, the glob**
is used, which results in copying everything from the/debug
folder, including subfolders. Folder structure is preserved in the copied output under the/web
directory. - Line 9: The
path
attribute is used for the second role performed by each path mapping entry; it defines the value of therequireJS
path that gets injected into themain.js
file. For the above example, therequireJS
path mapping is"persist":"libs/persist/min"
, which is relative to therequireJS
load root, typicallyappRootDir/web/js
. - Line 10 (Optional): The
cdnPath
attribute is optional and should only be used if you have actually put a copy of the library onto a CDN. If that is the case, then if thepath_mapping.json
file has theuse
attribute set to"cdn"
rather than"local"
, then the generatedrequireJS
path statement uses thiscdnPath
value rather than thepath
value (line 9). The path here is relative to the CDN root defined by the alias allocated to the CDN and used in thecdn
attribute for that library. If“use”
is set to“cdn”
but a library does not include CDN information, then the build falls back to using the local copy of the library and set up therequireJS
path accordingly.
-
-
Copy one of the existing library entries in the
"libs"
map and modify as needed for your library.The code sample below shows modifications for
my-library
, a library that contains both minified and debug versions.... "libs": { "my-library": { "cwd": "node_modules/my-library/dist", "debug": { "src": "my-library.debug.js", "path": "libs/my-library/my-library.debug.js" }, "release": { "src": "my-library.js", "path": "libs/my-library/my-library.js" } }, ...
In this example, the
cwd
attribute points to the location where NPM installed the library, thesrc
attribute points to a path or array of paths containing the files that are copied during a build, and thepath
attribute points to the destination that contains the built version.When defining your own library entry in the
path_mapping.json
file, you should test it out by running theojet build
command and then confirm that all the expected files have been copied into theappRootDir/web/js/libs
directory and that therequireJS
path mapping has been injected in the builtappRootDir/web/js/main.js
file.Note:
If the existing library entry that you copy to modify includes
"cdn": "3rdparty",
remove that line from the newly-created entry for your library. This line references the Oracle JET third-party area on the content distribution network (CDN) managed by Oracle. Your library won't be hosted there, and keeping this line causes a release build to fail at runtime by mapping your library's path to a non-existent URL.If you use a CDN, add the URL to the CDN in the entry for the
cdnPath
attribute. For information on working with libraries loaded from CDNs, see Understand the Path Mapping Script File and Configuration Options.
-
- If your project uses TypeScript, then you also need to define a
paths
entry in yourtsconfig.json
file that allows you to import the library using the same path name at design time as you'll use at runtime. If the library in question also provides some types for you to use, then the path should point to these to allow your editor to provide TypeScript support for your usage of that library. Here is an example of an existingpaths
entry in an Oracle JET app created with the command-line tooling."paths": { "ojs/*": [ "./node_modules/@oracle/oraclejet/dist/types/*" ], ...
-
-
If you didn’t use command-line tooling to create your app, perform the following steps to add the tool or library.
-
In the app’s
js/libs
directory, create a new directory and add the new library and any accompanying files to it.For example, for a library named
my-library
, create themy-library
directory and add themy-library.js
file and any needed files to it. Be sure to add the minified version if available. -
In your RequireJS bootstrap file, typically
main.js
, add a path for the library file in the path mapping section of therequirejs.config()
definition.For example, add the highlighted code below to your bootstrap file to use a library named
my-library
.requirejs.config({ // Path mappings for the logical module names paths: { 'knockout': 'libs/knockout/knockout-3.x.x', 'jquery': 'libs/jquery/jquery-3.x.x.min', ... 'text': 'libs/require/text', 'my-library': 'libs/my-library/my-library }, require(['knockout', 'my-library'], // this callback gets executed when all // required modules are loaded function(ko) { // Add any start-up code that you want here } );
For additional information about using RequireJS to manage your app's modules, see Use RequireJS for Modular Development.
- If your project uses TypeScript, then you also need to define a
paths
entry in yourtsconfig.json
file that allows you to import the library using the same path name at design time as you'll use at runtime. If the library in question also provides some types for you to use, then the path should point to these to allow your editor to provide TypeScript support for your usage of that library. Here is an example of an existingpaths
entry in an Oracle JET app created with the command-line tooling."paths": { "ojs/*": [ "./node_modules/@oracle/oraclejet/dist/types/*" ], ...
-
Troubleshoot the Addition of Third-Party Tools and Libraries
In most cases, when adding a third-party tool or library to your Oracle JET app, failures manifest in one of three ways:
- A 404 error in your browser-tools network panel.
If this occurs, you may have omitted a file from the set that you copied, or the library that you have selected has downstream dependencies and you need to define additional third-party libraries.
- An error dump on the browser console.
This is often an indication that the library is not in the correct format; see the following list of common problems for more information.
- Your usage of the library fails.
- The library is not a browser library.
Sometimes developers choose to use libraries that are intended for use in Node.js, rather than in a browser. Read the library’s documentation to ensure that it is a browser library.
- The library is in the wrong format.
There are multiple library formats in the JavaScript ecosystem, some of which are intended for use in browsers and others that are not. You may be unable to load a library that you found because it is in an incorrect format.
Check that your path mapping entry uses the correct distribution of the library out of its possible format options. For runtime use, you need modules that are in either AMD or UMD format. Alternatively, you can convert from one module format to another using external tools.
- The library has runtime dependencies.
The library might require you to include multiple dependencies into your
path_mapping.json
file so that all the required paths and modules are available at runtime. When loading a library at runtime, a 404 error is often a result of a missing downstream dependency.Read the library’s documentation, examine the
package.json
file for the library itself, and review the 404 error in detail to help figure out the error. - The library expects specific RequireJS path names.
The library that you are consuming may expect to be loaded from a specific path name, or it may expect a dependency to be set up in the same way. This exhibits a 404 error when trying to load a resource.
From the path submitted in the GET request, you should be able to figure out what is going on. Remember that you can configure the
path_mapping.json
file with the name of the path that must be created, so you should be able to correct this in the metadata to get everything working. -
The library needs a script tag to load code.
Most libraries are compatible with a module loading system and, assuming they are AMD compatible, will work with an import statement or
define()
andrequire()
methods. However, in some cases, you need to use a<script>
tag in your page to load the code, rather than have a module loader do it for you.A case where you may need to use a
<script>
tag is when the JavaScript library that you want to use is only available in ESM format. If you don't tranform it to AMD format, you import it into the root level of your project (theappRootDir/src/index.html
file) using a<script>
tag.Be cautious with code that requires a
<script>
tag like this, as it is probably likely to interact with the global JavaScript context in a way that is incompatible with modern module development. In cases like this, the library’s documentation should help point you in the right direction.
Troubleshoot RequireJS in an Oracle JET App
RequireJS issues are often related to modules used but not defined.
Use the following tips when troubleshooting issues with your Oracle JET app that you suspect may be due to RequireJS:
-
Check the JavaScript console for errors and warnings. If a certain object in the
oj
namespace is undefined, locate the module that contains it based on the information in About Oracle JET Module Organization or the Oracle JET Cookbook and add it to your app. -
If the components you specified using Knockout.js binding are not displayed and you are not seeing any errors or warnings, verify that you have added the
ojs/ojknockout
module to your app.
About JavaScript Partitions and RequireJS in an Oracle JET App
RequireJS supports JavaScript partitions that contain more than one module.
You must name all modules using the RequireJS bundles
option and supply a path mapping with the configuration options.
requirejs.config( { bundles: { 'commonComponents': ['ojL10n', 'ojtranslations/nls/ojtranslations', 'ojs/ojknockout', 'ojs/ojcomponentcore', 'ojs/ojbutton', 'ojs/ojpopup'], 'tabs': ['ojs/ojtabs', 'ojs/ojconveyorbelt'] } } );
In this example, two partition bundles are defined: commonComponents
and tabs
.
RequireJS ships with its own Optimizer tool for creating partitions and minifying JavaScript code. The tool is designed to be used at build time with a complete project that is already configured to use RequireJS. It analyzes all static dependencies and creates partitions out of modules that are always loaded together. The Oracle JET team recommends that you use an optimizer to minimize the number of HTTP requests needed to download the modules.
For additional information about the RequireJS Optimizer tool, see http://requirejs.org/docs/optimization.html.
For additional information about optimizing performance of Oracle JET apps, see Optimize Performance of Oracle JET Apps.