Integrate Oracle JET into Apex 5.0

Introduction

For Apex 5.1 it is announced that the new chart library will be Oracle JET. It is possible to use Oracle Jet already in Apex 5.0. How to integrate it is the scope of this article.

Rationale

I don’t see that most of the projects will switch very soon to Apex 5.1. Many have just made the transition to Apex 5 and Universal Theme or are about to do so. This was usually quite some work. It is difficult to argue for making a new conversion to Apex 5.1 very soon. Even if some of the reasons (Interactive Grids!) are extremly compelling. However since Oracle JET Charts will be one of the major areas where we can expect some adaptation when migrating, it makes sense to start working with that technology as soon as possible. Using Oracle JET charts in Apex 5.0 would be a very good preparation and might smooth a future upgrade path.

In the scope of the Apex Dashboard Competition I made a try to combine Oracle Jet into Apex 5.0. There are quite a few traps and surprises when trying to do so. I plan to do a series of blog posts that all deal with the combination of Apex 5 and oracle jet.

This article will just concentrate on how to install Oracle Jet (OJ) and make it available in Apex 5.0.

License considerations

Since a few weeks Oracle Jet is open source.
See : http://www.oracle.com/technetwork/developer-tools/jet/downloads/jetlicense-2905156.html

“Anyone and Everyone can use Oracle JET. It’s an Open Source project sponsored by Oracle. It is distributed under the Universal Permissive License(UPL).”

Essentially it means we can use and distribute oracle jet as long as the appropriate license document is included in the distribution. The license document is also part of the oraclejet.zip file that we need to download.

Download sources

To install Oracle JET, we first need to download the newest version. Since we do not create a Oracle JET only application, most of the “Getting Started” docs and templates are not really relevant. The oraclejet.zip file hides under the “Oracle JavaScript Extension Toolkit : Base Distribution” link in the download page.

Download page: http://www.oracle.com/technetwork/developer-tools/jet/downloads/index.html

General documentation links:

Installation

Preparation

Prerequisites: the oraclejet.zip file and an apex 5.0 database.

I tested the whole installation process in the vanilla oracle developer virtual machine. This VM includes an Oracle EE database 12.1.0.2, Apex 5.0, ORDS 3.0.x and SQL Developer 4.1.

http://www.oracle.com/technetwork/database/enterprise-edition/databaseappdev-vm-161299.html

Remember it is for evaluation purposes, but not for production development!

Server Installation Process

Unfortunatly there is no CDN link that we could simply include in our apex page. Instead we need to reference the needed js and css files somehow in our Apex page.

To install Oracle JET we need access to the apex web server, where the image files are located. To do so unzip the downloaded oraclejet.zip file into a usable folder. I suggest to use the normal apex/images/libraries folder.

e.g.

/apex/images/libraries/oraclejet

apex/images is typically mapped as the image path /i/. It can later be used with a substituition variable #IMAGE_PREFIX#. To access an oraclejet file, we could include it like this #IMAGE_PREFIX#libaries/oraclejet/libs/oj/v2.0.0/ojs.js .

Unzipping the file into some accessible folder is all we need for the server installation part.

OJ configuration and integration into Universal Theme (UT)

The oj documentation states that the require.js framework is mandatory if data visualization components are used. Since the charts are the most interesting parts for Apex, we are forced to use require.js. Most of the installation problems I encountered had to do with using require.js. More details are in the “About requirejs” part. I hope that in the distribution for Apex 5.1 this dependency will be gone.

About requirejs

Require.js is a tool that is able to load needed ressources dynamically. Typically those resources are other javascript libraries. If multiple parts of an application will load the same library, require.js will only load it once including dependencies.

The Oracle JET documentation tells us how to install requirejs.

The loader that makes it easier to manage library references and is designed to improve the speed and quality of your code. Oracle JET uses RequireJS by default for the Oracle JET QuickStart Template and is required if you plan to use Oracle JET’s internationalization, data visualization components, or the oj.OAuth plugin in your application.

To use RequireJS to manage references:

  1. Download Oracle JET as described in Download the Oracle JET Zip File.
  2. Copy js/libs/oj/version/main-template.js to the js folder.
  3. In the js folder, rename main-template.js to main.js.
  4. Add the following script reference to your index.html file:
  5. Update main.js as needed to reference Oracle JET modules or your own scripts.

    For a list of Oracle JET modules and additional details about using RequireJS in your Oracle JET application, see Using RequireJS for Modular Development.

For more information about RequireJS, see http://requirejs.org.

Ok, so lets do this. Do not copy the code for the main.js from the documentation. This code is outdated and does not have all the correct paths. The reason is simple. Some libraries, like oj itself have now a newer version and the version is included in the path. If you copy the main-template.js as stated in the documentation, you should be safe. This is what I used for the main.js file. The paths are relative to the location of the main.js file itself. in this case to set the baseUrl is not needed.

requirejs.config({
  // baseUrl: '#IMAGE_PREFIX#libraries/oraclejet/js',
  // Path mappings for the logical module names
  paths: {
    'knockout': 'libs/knockout/knockout-3.4.0',
    'jquery': 'libs/jquery/jquery-2.1.3.min',
    'jqueryui-amd': 'libs/jquery/jqueryui-amd-1.11.4.min',
    'ojs': 'libs/oj/v2.0.0/min',
    'ojL10n': 'libs/oj/v2.0.0/ojL10n',
    'ojtranslations': 'libs/oj/v2.0.0/resources',
    'signals': 'libs/js-signals/signals.min',
    'text': 'libs/require/text',
    'promise': 'libs/es6-promise/promise-1.0.0.min',
    'hammerjs': 'libs/hammer/hammer-2.0.4.min',
    'ojdnd': 'libs/dnd-polyfill/dnd-polyfill-1.0.0.min',
  },
  // Shim configurations for modules that do not expose AMD
  shim: {
    'jquery': {
      exports: ['jQuery', '$']
     }
    }
  },

// 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'
            }
        }
    }
});

/**
 * A top-level require call executed by the Application.
 * 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','ojs/ojpictochart'], // add additional JET component modules as needed
  function(oj, ko, $) // this callback gets executed when all required modules are loaded
  {
      // add any startup code that you want here
  }
);

This file will be included in our Apex page using a js script inclusion.

<script data-main="#IMAGE_PREFIX#libraries/oraclejet/js/main" 
src="#IMAGE_PREFIX#libraries/oraclejet/js/libs/require/require.js" 
></script>

Since the script tag needs a special data-main attribute, we can not use the normal Apex 5 mechanism of adding the js.file name to the appropriate page section.

Instead we need to modify the page template. I copied the minimal one.
The result looks like this.

...
<head>
  <meta charset="utf-8">  
  <title>#TITLE#</title>
  #APEX_CSS#
  #THEME_CSS#
  #TEMPLATE_CSS#
  #THEME_STYLE_CSS#
  #APPLICATION_CSS#
  <!-- Oracle JET CSS files -->
  <link rel="stylesheet" href="#IMAGE_PREFIX#libraries/oraclejet/css/libs/oj/v2.0.0/alta/oj-alta-min.css" type="text/css"/>
  <script data-main="#IMAGE_PREFIX#libraries/oraclejet/js/main" src="#IMAGE_PREFIX#libraries/oraclejet/js/libs/require/require.js" ></script>
    
  #PAGE_CSS#  
  #FAVICONS#
  #HEAD#
  
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
</head>
...

There is one more problem. Require.js does not like if a library with the same name is loaded previously without using require.js. (There are some notable exceptions that depend on using Asyncronous Module Definition Api (AMD)).

Apex 5.0 already loads the Hammer.js library in a different version than the one, that is included with the base Oracle JET distribution. Hammer.js provides the possibility to use Touch Gestures. So it is especially useful for mobile applications.

Because Apex already loaded Hammer require.js throws an error message. The only way I found to make it work, was to remove Hammer.js from the Theme. Unfortunately this can be done only by unsubscribing the Theme. After unsubscribing, we can edit the Theme and remove Hammer.js from the Javascript Files Section.

Thanks to Roel Hartmann for the tipp!

In any case you should check that the libraries were loaded successfully.

On firefox open the console (SHIFT+CTRL+K) and switch on the network traffic.

apex5_log_network

If all files loaded successfully then we are up and running.

It is time to try our first chart from the cookbook.

Using a static Cookbook Demo

So lets try to use the page and add a pictoChart to it

Link: http://www.oracle.com/webfolder/technetwork/jet/uiComponents-pictoChart-default.html

First we create a static region and add the html from the cookbook.

<div  id="picto-container">
  <div id='pc1' data-bind="ojComponent:{
      component: 'ojPictoChart',
      items: pictoChartItems,
      animationOnDisplay: 'auto',
      columnCount: 5
    }"
    style="vertical-align:middle; margin-right:15px">
  </div>
  <div style="display:inline-block; vertical-align:middle; font-weight:bold">
    <span style="color:#333333; font-size:1.1em">7 out of 10 college students</span><br>
    <span style="color:#ed6647; font-size:1.3em">have sleep problems.</span>
  </div>
</div>

Then we add some javascript functions to the page itself (global functions section).

In the javascript section we need to repeat the require call including the configuration part. This is because the libs are loaded asynchronously. On each page where we want to add some js files from Oracle Jet we would do a require call immediately before the relevant section. This time the baseUrl setting is helpful.

To enable pictocharts we need to require the ojs/ojpictochart lib.

require.config({
  baseUrl: '#IMAGE_PREFIX#libraries/oraclejet/js',
  paths: {
    'knockout': 'libs/knockout/knockout-3.4.0',
    'jquery': 'libs/jquery/jquery-2.1.3.min',
    'jqueryui-amd': 'libs/jquery/jqueryui-amd-1.11.4.min',
    'ojs': 'libs/oj/v2.0.0/min',
    'ojL10n': 'libs/oj/v2.0.0/ojL10n',
    'ojtranslations': 'libs/oj/v2.0.0/resources',
    'signals': 'libs/js-signals/signals.min',
    'text': 'libs/require/text',
    'promise': 'libs/es6-promise/promise-1.0.0.min',
    'hammerjs': 'libs/hammer/hammer-2.0.4.min',
    'ojdnd': 'libs/dnd-polyfill/dnd-polyfill-1.0.0.min'
  },
   // waitSeconds: 1,
  // Shim configurations for modules that do not expose AMD
  shim: {
    'jquery': {
      exports: ['jQuery', '$']
    }
  }
});      

require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout', 'ojs/ojpictochart'], function (oj, ko, $) {
    function PictoChartModel() {
      this.pictoChartItems = ko.observableArray([
        {name: 'Have  Sleep Problems', shape: 'human', count:7, color: '#ed6647'},
        {name: 'Sleep Well', shape: 'human', count: 3}
      ]);
    }
    var pictoChartModel = new PictoChartModel();

    $(document).ready(
      function() {
        ko.applyBindings(pictoChartModel, document.getElementById('picto-container'));
      }
    );
  });

Voilá we are done!

This is how the result looks like.
Screen Shot 2016-04-07 at 01.16.45

How to enhance the pictoCharts a bit including adding other shapes will be explained in another blog post.

Here are just two more screenshots to give you some ideas.

Screen Shot 2016-04-07 at 01.17.16Screen Shot 2016-04-07 at 00.28.13

13 thoughts on “Integrate Oracle JET into Apex 5.0

    • In fact I did both approches at the same time.

      I needed the config call including baseUrl because otherwise the sources were not loaded yet. Thanks for reminding me. I wanted to retest this approach without having to change the template by adding the script tag.

  1. Hi Sven,

    Nice write-up. Those charts are really cool, so including them makes total sense 🙂

    Just one suggestion; I would not put the Oracle JET files in the APEX images folder, instead I would recommend creating another directory for your own libraries outside of the APEX directory e.g. /c/ (from custom)
    With a substitution variable you can then reference that folder and where you call your files you can use &SUBSTITUTION_VAR. (instead of #IMAGE_PREFIX#) – so in case one day you change your path, you only have to change it in one place (the substitution string).

    The reason is that if you patch or upgrade APEX, it will overwrite the images folder and you will lose your own files. You can obviously put them again in there after you did the upgrade, but it would be an extra step in the process.

    Hope that helps,
    Dimitri

    • Thanks Dimitri for the nice comment. I think Oracle JET has great potential (mostly because of knockout.js) . It might change a lot of the typical ways that we are used working with Apex.

      With regards for the substitution variable, this is an intersting idea. My next plan was to simply a minimized zip file as a static ressource and refer to #APP_IMAGES#. I had major problems to get this going, but Carsten just confirmed that the folder structure should be kept. So I just have to identify what went wrong in my case.

  2. The index comment is copied from the JET documentation. It is part of a standard JET installation. In Apex we integrate the require js file into our Apex Template. This is described in the blog post an few lines further down.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.