Create custom widget JavaScript

You can customize JavaScript to add functionality for your widgets.

To add custom JavaScript to a widget you must create a JavaScript file under the extension-name/widget/widget-type/js directory. The name of the JavaScript file must match the value of the JavaScript property in the widget.json file, minus the .js extension. The convention is to use the widget-type as the JavaScript file name, without the .js suffix.

Note: As an easier coding option, there is JavaScript Code Layering feature that lets you extend the JavaScript of an Oracle Commerce provided widget with your own custom JavaScript. With the JavaScript Code Layering User Interface feature you can open an additional user interface that lets you layer custom JavaScript on top of the provided widget and which then has the benefit of staying on the provided widget. For more details on this feature, see Modify Your Storefront Using Code Editing Tools and Use the JavaScript Code Layering User Interface feature.

Custom JavaScript for a widget assumes that the file will perform some custom logic and return an object with extensions to the widget’s view model. The JavaScript file should implement the following format using RequireJS

Note: The module must be defined anonymously, in other words, have no package name defined in the module, as shown below.

define(
   // Dependencies
   ['jquery', 'knockout'],
   // Module Implementation
   function($,ko) {
             // We recommend enabling strict checking mode
      'use strict';
      // Private variables and functions can be defined here...
      var SOME_CONSTANT = 1024;
      var privateMethod = function () {
        // ...
      };
      return {
       // Widget JS
              // Some member variables...
              textInput: ko.observable(),
              // Some public methods...
              doSomethingWithInput: function () {
         //...

              }
     }
 });

The define statement above can be modified to include widget-specific libraries or other JavaScript files, if required. When a widget is instantiated all properties returned from the JavaScript file specified will be copied into that instance of the widget’s view model. This allows you to define properties, make Web API calls, or handle UI events.

Understand this

Using this in the custom JavaScript is suitable when you refer to the widget itself, but be careful of any callback methods where this may refer to a different context.

Include multiple JavaScript files

If your widget requires multiple JavaScript files, then any additional JavaScript files can be loaded through the dependencies in the widget module’s define statement.

To derive the path of the dependency, use the path js/ to reference the widget’s JavaScript folder followed by the path to the dependency but omitting the .js extension. The following example is of widget called myWidget includes the following JavaScript files:

myWidget/
        js/
           file1.js
           file2.js
           file3.js
           myWidget.js

And myWidget.js is the main JavaScript file for the widget and it is dependent on the other three JavaScript files, then the required dependencies in myWidget.js would look like the following example:

// Dependencies
 ['js/file1','js/file2','js/file3'],

Running custom logic upon widget instantiation

If you must run custom logic when a widget is instantiated, then add an onLoad() function to the widget JavaScript’s return object. The following is an example:

onLoad: function(widget) {
   //onLoad code here.
 }

onLoad() will run once the widget has finished loading and is populated with the necessary data. This is the main access point to configure data for the widget after its properties have been loaded and the system is ready to display the widget. As the onLoad() function is only called the first time the widget is instantiated, when returning to the same “page”, the widget does not need to be re-instantiated, so onLoad() is not called again.

Running custom logic each time a widget appears on a page

If you require some logic to run each time the widget appears on the page, add the beforeAppear() function to the widget JavaScript’s return object. The following is an example:

beforeAppear: function(page) {
          // Code to run before showing the widget here. }

beforeAppear() is run once any re-population of mapping data has occurred. This can be useful when a Web API call is required every time the widget is shown, or, some other functionality required every time the widget is shown.

Specify a function runs when an HTML element is being rendered

onRender is a custom Commerce binding that tells a function to run when an HTML element is being rendered on the page. The function is called in the current knockout context (typically bound to the widget, but certain knockout constructs, such as for each, may alter the binding context). For example, when the following div tag is rendered, the addEventHandlersForAnchorClick function is called:

<div id="CC-customerProfile" data-bind="onRender: addEventHandlersForAnchorClick">
            <!-- ... -->
 </div>

Rely on mapping for a property vs. initializing it via JavaScript

Most of a custom widget’s data will come from the JSON. This data does not need to be explicitly defined in the widget’s view model; knockout mapping will automatically create it.

For any data that doesn’t come through JSON, the observable should be explicitly defined in custom JavaScript. Otherwise, an error will be thrown if a template tries to render the property while it’s undefined. For example, imagine a productName widget that is configured with a productId and uses that productId to look up a product in order to display its name. In this case, the productId can be a property defined via koMapping as it should be returned by the JSON data. The product’s name, on the other hand, is expected to be populated when the product look up completes, so it needs to be defined in the JavaScript as productName: ko.observable(). Otherwise, when the template is rendered, an error will be thrown because productName is not a valid binding (it would be undefined).