Using Oracle JET to Create SCS Component – Part 1

I often receive question how to use Oracle JET framework for developing components for  Oracle Sites Cloud Service (SCS). JET provides ability to re-use its controls, collections and other UI elements thru the composite components. In this post I will explain how you can create a minimal SCS component that not only loads Oracle JET composite component on a site page, but also exposes JET Events and Methods as SCS Triggers and Actions, thus allowing JET component to interact with other SCS components on the same page.

Create SCS Component to Wrap JET Composite Component

In this post I will use Basic composite component that is described in the JET Cookbook to create sample SCS component with Oracle JET.

Start by creating new component and adding it to a site page for testing as described in the Getting Started with SCS Custom Components blog. I used name ‘JETCCA’ for this new component, but you can call it differently. Once you have new component, download and edit render.js file to replace the contents with the following code (instead of downloading a single file, you can use Oracle Documents desktop client to sync ‘JETCCA’ component to your desktop):

/* globals define, requirejs, SCSRenderAPI */
define(['knockout', 'jquery', 'text!./render.html'], function (ko, $, template) {
'use strict';
// ----------------------------------------------
// Define a Knockout ViewModel for your template
// ----------------------------------------------
var SampleComponentViewModel = function (args) {
var SitesSDK = args.SitesSDK;

// store the values passed in
this.id = args.id;
this.mode = args.viewMode || 'runtime';
this.divId = 'composite-component' + args.id;

// don't render anything until ready
this.initialized = ko.observable(false);

//
// Handle property changes from the Settings panel
//
this.updateCustomSettingsData = $.proxy(function (customData) {
// handle any settings changes
}, this);
this.updateSettings = function (settings) {
if (settings.property === 'customSettingsData') {
this.updateCustomSettingsData(settings.value);
}
};

// Register your updateSettings listener to recieve SETTINGS_UPDATED events
SitesSDK.subscribe(SitesSDK.MESSAGE_TYPES.SETTINGS_UPDATED, $.proxy(this.updateSettings, this));

//
// Get the initial settings data for the component and apply it
//
SitesSDK.getProperty('customSettingsData', this.updateCustomSettingsData);

};

// ----------------------------------------------
// Create a knockout based component implemention
// ----------------------------------------------
var SampleComponentImpl = function (args) {
// Initialze the custom component
this.init(args);
};
// initialize all the values within the component from the given argument values
SampleComponentImpl.prototype.init = function (args) {
this.createViewModel(args);
this.createTemplate(args);
this.setupCallbacks();
};
// create the viewModel from the initial values
SampleComponentImpl.prototype.createViewModel = function (args) {
// create the viewModel
this.viewModel = new SampleComponentViewModel(args);
};
// create the template based on the initial values
SampleComponentImpl.prototype.createTemplate = function (args) {
// create a unique ID for the div to add, this will be passed to the callback
this.contentId = args.id + '_content_' + args.viewMode;
// create a hidden custom component template that can be added to the DOM
this.template = '
<div id="' + this.contentId + '">' +
template +
'</div>
';
};
//
// SDK Callbacks
// setup the callbacks expected by the SDK API
//
SampleComponentImpl.prototype.setupCallbacks = function () {
//
// callback - render: add the component into the page
//
this.render = $.proxy(function (container) {
var $container = $(container);
// add the custom component template to the DOM
$container.append(this.template);
// apply the bindings
ko.applyBindings(this.viewModel, $('#' + this.contentId)[0]);
}, this);
//
// callback - dispose: cleanup after component when it is removed from the page
//
this.dispose = $.proxy(function () {
// nothing required for this sample since knockout disposal will automatically clean up the node
}, this);
};
// ----------------------------------------------
// Create the factory object for your component
// ----------------------------------------------
var sampleComponentFactory = {
createComponent: function (args, callback) {
// return a new instance of the component
return callback(new SampleComponentImpl(args));
}
};
return sampleComponentFactory;
});

From Oracle JET Cookbook site download the files that make up sample Basic composite component:

  • style.css
  • view.html
  • viewModel.js
  • metadata.json
  • loader.js
  • image files – card-background_1.png and debraphaely.png

Add these files to the ‘JETCCA’ component that you created. Navigate to your ‘JETCCA’ component in SCS Components catalog UI or on your desktop (if you synced your ‘JETCCA’ component) and then:

  • Open ‘assets‘ directory in your ‘JETCCA’ component
  • Create a ‘composites‘ folder under ‘assets‘ and navigate to it
  • Create a ‘demo-card‘ folder under ‘composites‘, navigate to it and add the following files to the ‘demo-card‘ folder:
    • styles.css
    • view.html
    • viewModel.js
    • metadata.json
    • loader.js
  • Create an ‘images’ folder under the ‘demo-card‘ folder and add the following filers:
    • card-background_1.png
    • debraphaely.png

After you have added file for “Basic” composite component, you need to add the JET dependencies and then update the render.js wrapper to call your JET component.

Managing Oracle JET Dependencies

Today SCS does not include Oracle JET by default. In the Sites Builder, we do include a minimum number of JET files but we do not support JET composite components or include all of JET.  Until we do support lazy loading of all of Oracle JET, it is your responsibility to ensure that all the Oracle JET dependencies they require are available to your component.

If you only have one JET composite component on a page, you may be able to bundle up the Oracle JET dependencies within this component.  However, you will, typically want to share the JET dependencies across components and ensure that each component doesn’t overwrite another’s configuration.

Including Oracle JET Files in the Design Time

In “Edit” and “Preview” mode in the Site Builder, SCS’s render.js file contains a number of Oracle JET files.  These files are used to assist in editing of the page but aren’t available at runtime.

In current 16.4.5 release, the gulped version of the SCS code in render.js contains the majority core JET files that you need to run “Basic” composite component.  The one file it doesn’t include is ojcomposite.js.  To run your vanilla “Basic” component, you will need to make the ojcomposite.js file available to the SCS build environment.

NOTE: In the 16.4.5 SCS build, the minified version of ojcomposite.js (and any other Oracle JET files) is not compatible with the minified version of the JET files that you can download fro Oracle JET site. You will need to use the debug version of ojcomposite.js when running in the Site Builder.  Since SCS does not include any Oracle JET in the runtime, you can use the minified version at runtime.

Due to the above issue, to include ojcomposite.js in the SCS build environment (Site Builder):

  • Open ‘assets‘ folder in the ‘JETCCA’ component that you created
  • Create an ‘oj‘ folder under ‘demo-card‘ and navigate to it
  • Add the following files to the ‘oj‘ folder:
    • Your JET debug version of ojcomposite.js file
    • oj-alta-notag-min.css (used later for rendering the flex layout)

Including Oracle JET Files in the Runtime

At runtime, there are no Oracle JET files included in SCS. You will need to manage all your JET dependencies that you want to share across your components by adding Oracle JET files you need and any dependencies via the ‘assets‘ directory of a theme that your site is using. the following is the minimum set of Oracle JET libraries and their dependent files that are not part of SCS’s runtime and as a result, will need to be included:

  • ojcore.js
  • ojcomposite.js
  • ojknockout.js
  • ojL10n.js
  • resources (JET resources directory)
  • jquery-ui (ojknockout has a dependency on jquery-ui/widget.js & jquery-ui/version.js)
  • promise.js

It has additional dependencies on the following JavaScript libraries that are already included in the SCS runtime:

  • knockout
  • jquery (ojknockout has a dependency on jquery)

To include the above files and dependencies into your SCS site:

  1. Create new site ‘JETSite’ using ‘StarterTemplate’. This will create both site and ‘JETSiteTheme’ theme.
  2. Using Oracle Documents desktop client sync ‘JETSIteTheme’ theme to your desktop
  3. Open theme directory on your desktop and create an ‘oj‘ folder under ‘assets
  4. Add all Oracle JET dependencies into the ‘assets/oj‘ folder:
    • ojcomposite.js
    • ojcore.js
    • ojknockout.js
    • ojL10n.js
    • promise.js
    • resources folder
      • nls folder
        • localeElements.js
        • ojtranslations.js
        • timezoneData.js
    • jquery-ui folder
      • widget.js
      • version.js
  5. Check that ‘JETSIteTheme’ has been synced to the Cloud.

Referencing the JET Composite Component

Now that you have created your ‘JETCCA’ component and added all the Oracle JET dependencies to SCS, you can update the render.js  file to point to your component. You will also need to let the SCS page know about your JET dependencies.

To include your JET composite component, navigagte to the ‘assets‘ directory of your ‘JETCCA’ component and:

  • Create a new file called: ‘render.html
  • Edit the ‘render.html‘ file and add the following to it (this code is taken from the JET cookbooks sample HTML for the ‘Basic’ composite component):
<!-- ko if: initialized -->
<div class="oj-flex oj-sm-flex-items-initial" data-bind="attr: {id: divId}, registerCCADemoCardEvents: true">
<!-- ko foreach: employees -->
<demo-card class="oj-flex-item" name="{{name}}" avatar="{{avatar}}" work-title="{{title}}"
work-number="{{work}}" email="{{email}}" background-image="{{backgroundImage}}" data-bind="attr: {'id': $parent.divId + 'card' + $index()}">
</demo-card>
<!-- /ko -->
</div>
<!-- /ko -->

Make sure that

  • The name of HTML file matches the entry in the ‘render.js‘ file that you updated earlier:
/* globals define, requirejs, SCSRenderAPI */
define(['knockout', 'jquery', 'text!./render.html'], function (ko, $, template) {
'use strict';
  • The ‘template‘ parameter above matches the entry in the ‘createTemplate’ function in the ‘render.js‘ file that you updated earlier:
// create the template based on the initial values
SampleComponentImpl.prototype.createTemplate = function (args) {
// create a unique ID for the div to add, this will be passed to the callback
this.contentId = args.id + '_content_' + args.viewMode;
// create a hidden custom component template that can be added to the DOM
this.template = '
<div id="' + this.contentId + '">' +
template +
'</div>
';
};

At this point, if you add ‘JETCCA’ component to a ‘JETSite’ site page and try to run your component, it will load but the JavaScript console in your browser would list a number of error about the Oracle JET dependency files not being found.

To reference Oracle JET files:

  • In your viewModel creation code, add in a request to get the ‘assetsURL‘ property using Sites SDK:
// setup the data after getting the assetsURL
SitesSDK.getProperty('assetsURL', $.proxy(function (assetsURL) {
}, this));

This will allow your to get hold of the URL for ‘assets/…/ojcomposite.js‘.

  • In the getProperty callback function, add the following:
var assetsRequirePath = 'scs-components-path' + assetsURL.replace('/_themes/_components', '').replace('/_compdelivery', ''),
ojLibs = SCSRenderAPI.getThemeUrlPrefix() + '/assets';

// for runtime, we need to require in all of JET
if ($.inArray(this.mode.toLowerCase(), ['edit', 'navigate']) !== -1) {
// we currently don't have OJComposite in the builder code
requirejs.config({
paths: {
// Oracle JET
'ojs/ojcomposite': assetsURL + '/oj/ojcomposite'
}
});

} else {
// udpdate the require config to include the expected JET paths
requirejs.config({
paths: {
// Oracle JET
'ojs': ojLibs + '/oj',
'ojL10n': ojLibs + '/oj/ojL10n',
'ojtranslations': ojLibs + '/oj/resources',
'ojalta-css': ojLibs + '/oj/css/alta/oj-alta-min',
'ojalta-css-notag': ojLibs + '/oj/css/alta/oj-alta-notag-min',
'promise': ojLibs + '/oj/promise',
'jquery-ui': ojLibs + '/oj/jquery-ui'
},
config: {
ojL10n: {
// locale: SCS.languageCode
}
},
map: {
'*': {
'jqueryui-amd': 'jquery-ui'
}
}
});
}

// now, dynamically require in the CCA component
require([assetsRequirePath + '/composites/demo-card/loader'], $.proxy(function (demoCard) {
// note that we can now render the CCA component
this.initialized(true);
}, this));

The code above does the following three things:

  1. When the SCS page is running in the builder (‘Edit’ or ‘Preview’ mode), it sets up the ‘ojs/ojcomposite‘ that the loader.js file loads in the requireJS config to point to the ojcomposite.js file that you placed in your ‘assets‘ folder
  2. When the SCS page is in runtime, it sets up all the requireJS config JET paths to point to the files you placed in your theme’s ‘assets‘ directory
  3. Once the requireJS config has been updated, it only then calls the JET composite component’s ‘loader.js‘ to load up the component and, once it’s available, notes that the component has been initialized so it will then show the contents within the ‘render.html‘ file

To the top of the same function (before you make the call to require in the ‘loader.js‘), add in the sample data to your SampleComponentViewModel object, which also references the ‘assetsURL‘.

// define the data for the composite
this.employees = [
{
name: 'Deb Raphaely',
avatar: assetsURL + '/composites/demo-card/images/debraphaely.png',
title: 'Purchasing Director',
work: '5171278899',
email: 'deb.raphaely@oracle.com',
backgroundImage: assetsURL + '/composites/demo-card/images/card-background_1.png'
},
{
name: 'Adam Fripp',
avatar: null,
title: 'IT Manager',
work: '6501232234',
email: 'adam.fripp@oracle.com',
backgroundImage: null
}
];

To validate that all is setup correctly, you can do the following:

  • Open the ‘JETSite’ site you previously created
  • Take a site page into ‘Edit’ mode
  • Click on the ‘Add’ option, navigate to ‘Custom’, select ‘JETCCA’ component that you created and drop it onto the page
    • At this point you should see the component render correctly as it does in the Oracle JET Cookbook sample. If it doesn’t, check the Console for errors
  • Switch to ‘Preview’ mode in the Site Builder and validate that your componet renders correctly.
    • If it switches from “side by side” to “one above the other” when you switch then the Alta CSS file isn’t being included.
    • Validate via the Console that it is being included in the site in the ‘Preview’ mode

At this point, your cards should render. However, if you click on the card, they won’t flip. This is because the Oracle JET sample assumes that the JQuery global has been defined. We will deal with that in the Part 2 of this blog post.

 

 

 

2 Comments

Leave a comment