Skip to main content

Creating an APEX plugin for an Oracle JET component - Part 2

In my previous blogpost I showed how you can embed an Oracle JET component in your APEX application. Now it is time to make a plugin out of the wisdom we gained doing so.
First of all a disclaimer. My intention is to make this plugin and the inner workings as simple as possible. So you can add a lot more functionality, checks etc and therefore add complexity. But this is intended to be as simple as possible.

The plugin consists of three parts: a PL/SQL render function, a snippet of JavaScript and a PL/SQL ajax function.

The render function is defined as :

function render 
( p_region                in  apex_plugin.t_region
, p_plugin                in  apex_plugin.t_plugin
, p_is_printer_friendly   in  boolean 
) return apex_plugin.t_region_render_result 

  c_region_static_id      constant varchar2(255)  := apex_escape.html_attribute( p_region.static_id );

  -- Add placeholder div
  sys.htp.p (
     '<div class="a-JET-PictoChart" id="' || c_region_static_id || '_region">' ||
       '<div class="a-JET-PictoChart-container" id="' || c_region_static_id || '_chart"></div>' ||
     '</div>' );
  -- Load the JavaScript library   
  ( p_name      => 'pictoChart'
  , p_directory => p_plugin.file_prefix
  -- Initialize the chart
  ( p_code => 'jet.picto.init('||
                  '"#'||c_region_static_id||'_chart", '          || -- pRegionId
                  '"' || apex_plugin.get_ajax_identifier ||'"'   || -- pApexAjaxIdentifier
  return null;
end render;

So what it does in these three steps is :
1. Generate a DIV placeholder, just as we saw in that previous post
2. Load the JavaScript file "pictoChart.js",
3. Execute the JavaScript function "jet.picto.init" providing two parameters, the regionId of the DIV and the (internal) identifier of the PL/SQL ajax function.

The contents of the JavaScript file is :

! function (jet, $, server, util, debug) {
    "use strict";
        baseUrl: apex_img_dir + "oraclejet/js/libs",
        paths: {
            "jquery": "jquery/jquery-2.1.3.min",
            "jqueryui-amd": "jquery/jqueryui-amd-1.11.4.min",
            "ojs": "oj/v2.0.0/min",
            "ojL10n": "oj/v2.0.0/ojL10n",
            "ojtranslations": "oj/v2.0.0/resources",
            "promise": "es6-promise/promise-1.0.0.min"
        shim: {
            jquery: {
                exports: ["jQuery", "$"]
    }), jet.picto = {
        init: function (pRegionId, pApexAjaxIdentifier) {
            require(["ojs/ojcore", "jquery", "ojs/ojpictochart"], function (oj, $) {
                server.plugin(pApexAjaxIdentifier, {}, {
                    success: function (pData) {
}(window.jet = window.jet || {}, apex.jQuery, apex.server, apex.util, apex.debug);
// To keep ThemeRoller working properly:
define("jquery", [], function () {
    return apex.jQuery

The first function call (requirejs.config) is again the same is we did earlier. Please note that the paths mentioned in here might differ in your environment. You could define those paths as (Application level) Component Settings, but for simplicity I keep them hardcoded in this example.
Notice there is a "jet" namespace defined and within that namespace a nested "picto" namespace. So we could easily extend this file (after renaming it) to other chart types. The "init" method defines the required files and then calls apex.server.plugin passing the PL/SQL ajax function as a parameter. After this ajax function is called, the result - a JSON object - is passed to the ojPictoChart function.
On the last line, as Kyle Hu correctly commented in my previous post, we have to define jquery in order to make ThemeRoller work (again).
So in fact, the JavaScript is very straightforward: in the end just an ajax call passing the result into the ojPictoChart function.

So what is the magic of the last piece, the ajax PL/SQL function? Here it is::

function ajax
( p_region    in  apex_plugin.t_region
, p_plugin    in  apex_plugin.t_plugin 
) return apex_plugin.t_region_ajax_result
  c       sys_refcursor;
  l_query varchar2(32767);
  l_query := p_region.source;
  open c for l_query;
  apex_json.write('items', c);

  -- add settings
  apex_json.write('animationOnDisplay' , p_region.attribute_01);
  apex_json.write('columnCount'        , p_region.attribute_02);
  apex_json.write('layout'             , p_region.attribute_03);

  return null;
end ajax;

Thus it just takes the region source - a SQL statement -, executes it returning a JSON object. And to these results three of the available options, defined as plugin attributes, are added. Again, as simple as possible. So no validation on the correctness of the SQL (as all APEX Developers can write a correct SQL statement, right). And just a minor part of the available options are exposed in the plugin.

When you create a region based on this plugin you have to provide it with a correct SQL statement - one that will return a valid JSON object according to the pictoChart docs. For example:

select ename||' - '||job||'@'||dname "name"
,      'human' "shape"
,      1 "count"
,      case job
       when 'PRESIDENT' then 'black'
       when 'ANALYST'   then 'blue'
       when 'CLERK'     then 'green'
       when 'MANAGER'   then 'red'
       when 'SALESMAN'  then 'yellow'
       end "color"
from   emp
       join dept on emp.deptno = dept.deptno
order by 2 desc, 4

And if you set the settings as :

You'll get as a beautiful result:
So knowing this, it wouldn't be hard to rebuild the plugin by yourself. But for the lazy readers out there, you can download it here too.
Or probably build another plugin for another JET component!

Post a Comment

Popular posts from this blog

Push changed rows to an Interactive Grid

For pushing changes from the database to the end user, the regular solution is using websockets. A change in a record is detected - using a trigger or using the CQN (Change Query Notification) feature - and a notification is send to a websocket server. That websocket server broadcasts the notification over a channel to all browsers that are tuned in to that websocket channel. Then the browser reacts to that notification, usually showing an alert or refreshing a report. This trick is described on multiple sites, just Google for "oracle apex websockets" or similar.

So back in the old days, we used that notification in the browser to refresh the (interactive) report. But along comes the Interactive Grid (IG). While he full-refresh mechanism still works for IG, an IG has also the option to refresh just one row.  So wouldn't it be awesome that just the changed row(s) get refreshed upon a change in the database, instead of the whole report? Can we do it ... yes we can!
First i…

A review of APEX World 2017 - Day 1

Last week the SS Rotterdam was the beautiful location of the largest gathering of APEX Developers worldwide. With around 380 (!) attendees a new high was set. And they came from all over the world : I spotted people from The Netherlands, Belgium, Switzerland, Austria, Croatia, Germany, Denmark, Norway, UK, Ireland and the USA. And I even might have missed one or two ….

The event started with a presentation by the “father of APEX”, Mike Hichwa, talking about "Oracle APEX Past, Present and Future”. Of course everyone is curious what the APEX future might bring: Friendly URL’s, automated testing, more JSON, concurrent APEX versions, third party Oauth 2 authentication (think Facebook, Google), APEX app diff and more, a lot more, REST capabilities. And now we have to wait for APEX 5.2 … and that might take a while! 
After this keynote, the conference split up in three tracks. After the coffee break I returned to to big theatre where Geertjan Wielenga talked about "Finally Javas…

Dockerize your APEX development environment

Nowadays Docker is everywhere. It is one of the main components of Continuous Integration / Continuous Development environments. That alone indicates Docker has to be seen more as a Software Delivery Platform than as a replacement of a virtual machine.

However ...

If you are running an Oracle database using Docker on your local machine to develop some APEX application, you will probably not move that container is a whole to test and production environments. Because in that case you would not only deliver a new APEX application to the production environment - which is a good thing - but also overwrite the data in production with the data from your development environment. And that won't make your users very excited.
So in this set up you will be using Docker as a replacement of a Virtual Machine and not as a Delivery Platform.
And that's exactly the way Martin is using it as he described in this recent blog post. It is an ideal way to get up and running with an Oracle database …