UI Customizations with Oracle JET in Oracle WebCenter Sites 12c

image

Before You Begin

This blog assumes that you're familiar with Oracle JET, its custom modules, and overriding the default path/suffix configuration for JET modules so that you can include them as assets in your WebCenter Sites implementation. Otherwise, you may want to begin by reading this blog: Creating Oracle JET Modules as Assets in Oracle WebCenter Sites.

Overview

WCS 11g’s Admin and Contributor UIs are built with a version of Dojo that does not support AMD modules out-of-the-box. This means if you’re customizing the UI with Oracle JET modules, you can include requireJS as you would normally for Oracle JET. However, 12c’s version of Dojo does support AMD modules, so you can no longer include requireJS for your Oracle JET modules. (If you try, both Dojo and requireJS declare the “define” and “require” functions, which then conflict with each other and your code breaks.) The solution for 12c is to use Dojo’s module loader instead of requireJS. You shouldn’t have to change your JET modules, you just need to update the Oracle JET and Dojo configurations. 

Steps and Examples

In 11g, you used to be able to load an Oracle JET module like this:
 
<html>
<head>
<title>Oracle JET Example</title>
<link rel="stylesheet" href="css/oj/libs/oj/v2.3.0/alta/oj-alta-min.css" type="text/css"/>
<script src="js/oj/libs/require/require.js"></script>
<script language="javascript"><!--
require.config({
  baseUrl: 'js/oj',
  paths:
  {
    'knockout': 'libs/knockout/knockout-3.4.0.debug',
    'jquery': 'libs/jquery/jquery-3.1.0',
    'jqueryui-amd': 'libs/jquery/jqueryui-amd-1.12.0',
    'promise': 'libs/es6-promise/es6-promise',
    'hammerjs': 'libs/hammer/hammer-2.0.8',
    'ojdnd': 'libs/dnd-polyfill/dnd-polyfill-1.0.0',
    'ojs': 'libs/oj/v2.3.0/debug',
    'ojL10n': 'libs/oj/v2.3.0/ojL10n',
    'ojtranslations': 'libs/oj/v2.3.0/resources',
    'text': 'libs/require/text',
    'signals': 'libs/js-signals/signals',
    'customElements': 'libs/webcomponents/CustomElements',
    'proj4': 'libs/proj4js/dist/proj4-src',
    'css': 'libs/require-css/css',
  }
  ,
  shim: {
    'jquery': {
      exports: ['jQuery', '$']
    }
  }
});
require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout', 'ojs/ojmodule', 'ojs/ojinputtext'],
  function (oj, ko, $) {
     $(document).ready(function () {
        oj.ModuleBinding.defaults['modelPath'] = "ContentServer?d=&pagename=oj/modules/";
        oj.ModuleBinding.defaults['viewPath'] = "text!ContentServer?d=&pagename=oj/modules/";
        oj.ModuleBinding.defaults['viewSuffix'] = "View"; 
        ko.applyBindings();
     });      
  }
);
//-->
</script>
</head>
<body style="padding:25px;">
<h1>Oracle JET Example</h1>
<div data-bind="ojModule: { name: 'MyInputText'}"></div>
</body>
</html>
 
Now, in 12c, you have to do the following (note we are not including require.js, and also note there is a ./ before the “ContentServer?...” URLs):
 
<html>
<head>
<title>Oracle JET Example</title>
<link rel="stylesheet" href="css/oj/libs/oj/v2.3.0/alta/oj-alta-min.css" type="text/css"/>
<script language="javascript"><!--
require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout', 'ojs/ojmodule', 'ojs/ojinputtext'],
  function (oj, ko, $) {
     $(document).ready(function () {
        oj.ModuleBinding.defaults['modelPath'] = "./ContentServer?d=&pagename=oj/modules/";
        oj.ModuleBinding.defaults['viewPath'] = "text!./ContentServer?d=&pagename=oj/modules/";
        oj.ModuleBinding.defaults['viewSuffix'] = "View"; 
        ko.applyBindings();
     });      
  }
);
//-->
</script>
</head>
<body style="padding:25px;">
<h1>Oracle JET Example</h1>
<div data-bind="ojModule: { name: 'MyInputText'}"></div>
</body>
</html>
 
And you MUST add Dojo configuration before dojo.js is included, which means updating the UI/LayoutHtml element. So, create a new element called CustomElements/UI/LayoutHtml with the following contents (adding the text in bold is the only change):
 
<%@ taglib prefix="cs" uri="futuretense_cs/ftcs1_0.tld"
%><%@ taglib prefix="ics" uri="futuretense_cs/ics.tld"
%><%@ taglib prefix="xlat" uri="futuretense_cs/xlat.tld"
%><%@ taglib prefix="controller" uri="futuretense_cs/controller.tld"
%><%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"
%><%@page import="org.apache.commons.lang.StringUtils"
%><cs:ftcs><!DOCTYPE html>
<%
String locale = ics.GetSSVar("locale").replace("_", "-");
locale = StringUtils.isBlank(locale) ? "en-US" : locale;
boolean rtl = Boolean.valueOf(ics.GetSSVar("supportRTL"));
if(rtl) {
%>

<html lang="<%=locale%>" dir="rtl">
<% } else { %>
<html lang="<%=locale%>">
<%
}
locale = locale.toLowerCase();
%>

<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" type="text/css" href="js/fw/css/ui/fw.css"/>
<link rel="stylesheet" type="text/css" href="js/fw/css-flat/ui/fw.css"/>
<link rel="stylesheet" type="text/css" href="js/fw/css/locale/<%=locale%>.css"/>
<% if(rtl) { %>
<link rel="stylesheet" type="text/css" href="js/fw/css/ui/fw_rtl.css"/>
<% } %>

    <ics:callelement element="OpenMarket/Xcelerate/UIFramework/SetEventHandlers"/>
  
</head>
<!-- 
With the introdution of the new DND Uploader  we need Listen in UIContributor Main Body element 
To accept a drop, the drop target has to listen to at least three events.
The dragenter event, which is used to determine whether or not the drop target is to accept the drop. If the drop is to be accepted, then this event has to be canceled.
The dragover event, which is used to determine what feedback is to be shown to the user. 
If the event is canceled, then the feedback (typically the cursor) is updated based on the dropEffect attribute's value.
Finally, the drop event, which allows the actual drop to be performed.
Following is the example to drop an object into another object:
ondragenter="return dragEnter(event)" 
ondrop="return dragDrop(event)" 
ondragover="return dragOver(event)"
     
Only need to capture and handle ondragover="return dragOver(event)" 
 -->
<body class="fw" id="uicontributor"  ondragover="return dragOver(event)" >
<div id="loadingBack">
<div id="loadingFront">
<img src="wemresources/images/ui/wem/loading.gif" alt="Loading..." height="32" width="32" /><br />
<xlat:stream key="dvin/UI/Loadingdotdotdot"/>
</div>
</div>

<script language="javascript"><!--
var dojoConfig = {
  baseUrl: 'js/dojo/',
  packages: [
   { name: "knockout", location: "../oj/libs/knockout", main: "knockout-3.4.0.debug" },
   { name: "jquery", location: "../oj/libs/jquery", main: "jquery-3.1.0" },
   { name: "jqueryui-amd", location: "../oj/libs/jquery/jqueryui-amd-1.12.0", main: "../jqueryui-amd-1.12.0" },
   { name: "promise", location: "../oj/libs/es6-promise", main: "es6-promise" },
   { name: "hammerjs", location: "../oj/libs/hammer", main: "hammer-2.0.8" },
   { name: "ojdnd", location: "../oj/libs/dnd-polyfill", main: "dnd-polyfill-1.0.0" },
   { name: "ojs", location: "../oj/libs/oj/v2.3.0/debug", main: "oj" },
   { name: "ojL10n", location: "../oj/libs/oj/v2.3.0/debug", main: "ojL10n" },
   { name: "ojtranslations", location: "../oj/libs/oj/v2.3.0/resources", main: "nls/ojtranslations" },
   { name: "text", location: "../oj/libs/require", main: "text" },
   { name: "signals", location: "../oj/libs/js-signals", main: "signals" },
   { name: "customElements", location: "../oj/libs/webcomponents", main: "CustomElements" },
   { name: "proj4", location: "../oj/libs/proj4js/dist", main: "proj4-src" },
   { name: "css", location: "../oj/libs/require-css", main: "css" }
  ]
};
//-->
</script>

<script type="text/javascript" src="js/dojo/dojo.js" data-dojo-config="parseOnLoad:true, locale:'<%=locale%>', fw_csPath:'<ics:getproperty name="ft.cgipath" />'"></script>
<script type="text/javascript" src="ImageEditor/clarkii/clarkii_config.js"></script>
<script type="text/javascript" src="js/fw/fw_ui.js"></script>
<script type="text/javascript" src="ckeditor/ckeditor.js" defer="defer"></script>
<script>
dojo.require("fw.ui._base");
</script>
<div data-dojo-type="dijit.layout.BorderContainer" id="topcontainer" style="width:100%;height:100%;padding:0" data-dojo-props="onLoad:function(){ loadCheck(); }">
<div data-dojo-type="dijit.layout.ContentPane" id="topPane" style="padding:0;" data-dojo-props="region:'top','class':'headerContentPane'">
<controller:callelement elementname="${layoutconfig['header']}" responsetype="${layoutconfig['header[@responsetype]']}" />
</div>
<div data-dojo-type="dijit.layout.BorderContainer" id="treeContainer" data-dojo-props="region:'leading',minSize:1,maxSize:500,splitter:true,design:'sidebar',preload:true,'class':'treeContainer'">
<controller:callelement elementname="${layoutconfig['leftnavigation']}" responsetype="${layoutconfig['leftnavigation[@responsetype]']}" />
</div>
<div data-dojo-type="fw.dijit.layout.UITabContainer" id="centerTabContainer" data-dojo-props="region:'center'">
<controller:callelement elementname="${layoutconfig['centerpane']}" responsetype="${layoutconfig['centerpane[@responsetype]']}" />
</div>
<div data-dojo-type="dijit.layout.ContentPane" id="rightPane" style="display:none" data-dojo-props="region:'trailing'">
<controller:callelement elementname="${layoutconfig['rightnavigation']}" responsetype="${layoutconfig['rightnavigation[@responsetype]']}" />
</div>
</div>
</body>
</html>
</cs:ftcs>
 
Finally, there are two JET javascript files you need to update. Unfortunately, Dojo’s “define” function does not understand requireJS’s “ojL10n!xxx” syntax for localizations, and therefore Dojo throws an xhrInjectFailed error when trying to parse it. So, as a workaround, we need to remove the localization feature from ojcore.js and ojvalidation.js. (Other workarounds are of course possible, depending on your requirements.)
 
At the top of oj/libs/oj/v2.3.0/debug/ojcore.js, change the line:
 
define(['require', 'ojL10n!ojtranslations/nls/ojtranslations', 'promise'], function(require, ojt)
 
to 
 
define(['require', 'ojtranslations/nls/ojtranslations', 'promise'], function(require, ojt)
 
Next, at the top of oj/libs/oj/v2.3.0/debug/ojvalidation.js, change the line:
 
define(['ojs/ojcore', 'jquery', 'ojL10n!ojtranslations/nls/localeElements', 'ojs/ojmessaging'], function(oj, $, ojld)
 
to:
 
define(['ojs/ojcore', 'jquery', 'ojtranslations/nls/localeElements', 'ojs/ojmessaging'], function(oj, $, ojld)
 
This just defaults all the localized variables in JET to “en” (this default is set in ojL10n.js). It doesn’t affect any other functionality. If you use the localization features from requireJS, you will need to implement a different workaround.
 
Be sure to redeploy ojcore.js and ojvalidation.js to your webapp once you update these files.
 
Now, you should be able to include your Oracle JET modules in 12c’s UI without any errors.

 

Subscribe to Our Newsletter

Stay In Touch