This article was written by Anand Yerrapati from the Oracle Cloud Solution Engineering team.

Visual Builder provides multiple level of reusability in the UI layer, including application and page templates, reusable page fragment, and reusable action chains. This article explains how to create a reusable custom component that you can add to multiple pages in your applications. Our sample component consists of an input text with action buttons Cancel and Accept.

Custom Component

Create a custom component

Start by creating a new web application. Then to create e new component:

  • Open application structure under web apps
  • Expand Resources
  • Right-click on the “components” folder and select Create option
  • Create Component Menu
  • Enter an id value for the custom component in the input text field on the dialog opened (we used custom-input-text in our sample component)
  • Click on Create button
  • This step will create a folder with a set of files as shown below

Component Directory and Files

  • Here is an explanation of each of these files
    • loader.js: This file helps to register the custom component with the application. So that, it can be used on any page in the application just like any other common components
    • component.json: This file helps to configure the parameters, methods, and events used in the custom component and exposed outside of the component
    • <name>-view.html: This file defines the view of the component which will be the face of the custom component
    • <name>-viewModel.js: This file handles the logic for the components in the view
    • <name>-style.css: This file styles the custom components with the CSS

Building the custom component

Our goal in this article is to create a custom component that shows text with an edit icon and allows in-place editing as shown below.

Custom component in action

  • Open the component.json file
  • Add the below properties required for the custom component

  "properties": {
    "hintText" : {
      "type" : "string"
    },
    "inputText" : {
      "type" : "string"
    }
  }

  • Add the below custom event to trigger back when there is a change in the text

"events": {
     "inputTextChanged" : {
      "description" : "The event for on click check (ok) button",
      "bubbles" : true,
      "cancelable" : false,
      "detail" : {
        "newText" : {"type" : "string"}
      }
    }
  }

  • After making these changes the component.json will look like as below

{
  "name": "custom-input-text",
  "displayName": "custom-input-text",
  "description": "A description of custom-input-text.",
  "version": "1.0.0",
  "jetVersion": ">= 9.0.0",
  "properties": {
    "hintText" : {
      "type" : "string"
    },
    "inputText" : {
      "type" : "string"
    }
  },
  "methods": {},
  "events": {
     "inputTextChanged" : {
      "description" : "The event for on click check (ok) button",
      "bubbles" : true,
      "cancelable" : false,
      "detail" : {
        "newText" : {"type" : "string"}
      }
    }
  },
  "slots": {}
}

Open custom-input-text-view.html file and add the below HTML code

<div>
  <oj-bind-if test="[[!editMode()]]">
    <oj-bind-text value="[[inputText]]"></oj-bind-text>
    <oj-button chroming="borderless" id="btEdit" display="icons" on-oj-action="[[ onEditClick ]]">
      <span class="oj-ux-ico-edit" slot="startIcon"></span>
    </oj-button>
  </oj-bind-if>
  <oj-bind-if test="[[editMode()]]">
    <div class="oj-flex">
    <oj-input-text value="{{ $properties.inputText }}" placeholder="[[$properties.hintText]]" style="width: 200px;"></oj-input-text>
    <oj-button chroming="borderless" id="btCancel" display="icons" on-oj-action="[[ onCloseClick ]]">
      <span class="oj-ux-ico-close" slot="startIcon"></span>
    </oj-button>
    <oj-button chroming="borderless" id="btSubmit" display="icons" on-oj-action="[[ onCheckClick ]]">
      <span class="oj-ux-ico-check" slot="startIcon"></span>
    </oj-button>
    </div>
  </oj-bind-if>
</div>

 

Open custom-input-text-viewModel.js file and replace the code with the below

define([
  'knockout',
  'ojs/ojcontext',
  "ojs/ojinputtext"
], (
  ko,
  ojContext
) => {
  'use strict';

  class CustomInputTextComponentModel {
    constructor(context) {

      // At the start of your viewModel constructor.
      var busyContext = ojContext.getContext(context.element).getBusyContext();
      var options = { 'description': 'Component Startup - Waiting for data' };
      var self = this;

      self.busyResolve = busyContext.addBusyState(options);
      self.composite = context.element;
      self.properties = context.properties;

      self.editMode = ko.observable(false);
      // Example observable.
      self.inputText = ko.observable(self.properties.inputText);
      // Once all startup and async activities have finished, relocate if there are any async activities.
      self.busyResolve();
      self.onEditClick = (event) => {
        self.composite.addEventListener('ojAction', function (event) {
          if (event.srcElement.id === "btCancel")
            self.onCloseClick(event);
          else if (event.srcElement.id === "btSubmit")
            self.onCheckClick(event);
        });
        self.editMode(true);
      };

      self.onCloseClick = (event) => {
        self.editMode(false);
      };
      self.onCheckClick = (event) => {
        if (self.editMode()) {
          var eventParams = {
            'bubbles': true,
            'cancelable': false,
            'detail': {
              'newText': self.inputText()
            }
          };
          //Raise the custom event
          self.composite.dispatchEvent(new CustomEvent('inputTextChanged', eventParams));
          self.editMode(false);
        }
      };
    }
  }//End of the class CustomInputTextComponentModel

  return CustomInputTextComponentModel;
});

Your custom component is ready.

How to use the custom component

  • Open the main page of the application
  • Under Components, search for the “custom-input-text”
  • Drag & Drop the components from the search result to the code editor
  • Fill in the properties below
    • hint-text : "Hello Text"
    • input-text:  "[[ $variables.mytext ]]"    — (mytext is a string variable)
  • Create an event for "on-input-text-changed"
  • Go to the action chain created for the event
  • Drag & Drop “Fire Notification” components on the design
  • Enter [[ $variables.newValue.newText ]] for summary and select Notification Type as “info” 
  • Now the application is ready to run to test the custom component

 

Explanation of the custom component Behavior

So far, we have seen how to create and use the custom component. In this section, we will understand what happens in the flow.

  • When the application is run, the component will display the given text (value of “input-text” property) with an edit icon.

Read Mode

  • On click of the edit icon, it will show an input text component filled with the given text and two buttons Cancel and Accept

Edit Mode

  • On click of the Cancel button (X), it will close the current view of the input text and displays the previous text with the edit icon
  • On click of Accept button (), it will trigger a custom event (on-input-text-changed) with the new text and it will close the current view of the input text and displays the new text with the edit icon.
  • When we create an event listener for the component, the new text will be passed to the corresponding action chain
  • In this example, we are showing that text on a notification

Notification Results

 

How to import a custom component

 

While we created the custom component directly in an app, you can also create components outside of Visual Builder creating the same directory and files that we created above. You can then zip the directory to create a zip file that other developers can add to their application to use your component. To import a custom component: 

  • Expand the application structure in VBCS
  • Under Resources, right-click on components
  • Select the Import option

Import component menu

 

  • Upload the zip file of the custom component
  • Click on the Import button
  • You will be able to see the folder structure below for the custom component

 

Conclusion

As you can see creating reusable custom components is quite simple. This can help you extend the functionality your development team can create in their user interfaces beyond the set of components provided out of the box by Visual Builder. To learn more on creating custom components check out Duncan's custom components learning path series of blog entries.