X

Best Practices from Oracle Development's A‑Team

Using Process Cloud Service REST API Part 3

John Featherly
Cloud Native Architect

Introduction

In this the third and final part of the series on using the PCS Rest API we will take the single page HTML application we started with in Part 1 and use Oracle JET (Javascript Extension Toolkit) to create an HTML/js application. Oracle JET is also an MVVM (model-view-viewmodel) architecture as we saw in Part 2. JET using Knockout.js for the ViewModel instead of Vue.js we used in Part 2. There is an excellent overview in the JET documentation where you will find the following diagram.

JET architecture

Setup Project in NetBeans

Using NetBeans as we did in Part 2 create a new project using the HTML5/Javascript category as before. This time we will use a template file for the Oracle JET NavDrawer application template.

NetBeans-template-project-start Name the project PCSrestJET as shown.

projectname

The JET NavDrawer template is available here. Download the zip file and select it in the template dialog shown below.

template-selection

The application is created and functional, right click on the index.html file and select "Run File" and it will open in a Chrome window.

NetBeans-Project

Create the Application

The NavDrawer template has four pages named Dashboard, Incidents, Customers and About. When the browser window displaying the application is narrow, the NavBar showing the four pages collapses to a "hamburger" menu that slides out from the left. The NavBar menu looks like

navbar-template

and the flyout menu looks like

hamburger-flyout

Implementing each PCS REST call we've been working with in previous posts in this series we'll map each call to a page in the new application. The PCS calls are for Processes, Instances and Tasks so let's change Dashboard -> Processes, Incidents -> Instances and Customers -> Tasks. The ViewModel for the overall application is in js/appController.js where we'll make the modifications as well as setting the application name and user login name.

In js/appController.js locate the section for the page names and routing and edit as shown below.

       // Router setup
       self.router = oj.Router.rootInstance;
       self.router.configure({
         'processes': {label: 'Processes', isDefault: true},
         'instances': {label: 'Instances'},
         'tasks': {label: 'Tasks'},
         'about': {label: 'About'}
       });
      oj.Router.defaults['urlAdapter'] = new oj.Router.urlParamAdapter();
      // Navigation setup
      var navData = [
      {name: 'Processes', id: 'processes',
       iconClass: 'oj-navigationlist-item-icon demo-icon-font-24 demo-chart-icon-24'},
      {name: 'Instances', id: 'instances',
       iconClass: 'oj-navigationlist-item-icon demo-icon-font-24 demo-fire-icon-24'},
      {name: 'Tasks', id: 'tasks',
       iconClass: 'oj-navigationlist-item-icon demo-icon-font-24 demo-people-icon-24'},
      {name: 'About', id: 'about',
       iconClass: 'oj-navigationlist-item-icon demo-icon-font-24 demo-info-icon-24'}
      ];
      self.navDataSource = new oj.ArrayTableDataSource(navData, {idAttribute: 'id'});
      // Drawer
      // Called by nav drawer option change events so we can close drawer after selection
      self.navChangeHandler = function (event, data) {
       if (data.option === 'selection' && data.value !== self.router.stateId()) {
         self.toggleDrawer();
       }
      }
      // Close offcanvas on medium and larger screens
      self.mdScreen.subscribe(function() {oj.OffcanvasUtils.close(self.drawerParams);});
      self.drawerParams = {
        displayMode: 'push',
        selector: '#navDrawer',
        content: '#pageContent'
      };
      // Called by navigation drawer toggle button and after selection of nav drawer item
      self.toggleDrawer = function() {
        return oj.OffcanvasUtils.toggle(self.drawerParams);
      }
      // Header
      // Application Name used in Branding Area
      self.appName = ko.observable("PCS REST API Demo");
      // User Info used in Global Navigation area
      self.userLogin = ko.observable("John");

Notice changes to navData to rename the pages, also note the application name has been set to "PCS REST API Demo" and userLogin has been set to "John". We are working with an MVVM application architecture here so we can easily adapt the work we did in Part 2 to create the Views and ViewModels for each page of the application. The files already exist from the template in js/views and js/viewModels. The file names are not what we want, dashboard, incidents and customers so rename them (both the view .html files and the viewModel .js files) to processes, instances and tasks.

Knockout.js

Although what we learned about ViewModels with vue.js is generally applicable it is worthwhile looking at introductory documentation for Knockout.js if you are not familiar with it already. Starting with the Processes page, the View or HTML construct we used before was a <ul> list. Let's change it a bit and use an HTML <table> this time. Open the processes.html view file and insert the <table> HTML code inside the oj-hybrid-padding <div> as shown.

<div class="oj-hybrid-padding">
  <h3>Part 1</h3>
  <p>Use the process-definitions call to get a list of registered process names and definition IDs</p>
  <h4>Registered Processes</h4>
  <table>
      <thead>
          <tr><th>Process DefId</th><th>Process Name</th><th>Version</th></tr>
      </thead>
      <tbody data-bind="foreach: proclist">
          <tr>
              <td data-bind="text: processDefId"></td>
              <td data-bind="text: processName"></td>
              <td data-bind="text: revision"></td>
          </tr>
      </tbody>
  </table>
</div>

Notice the Knockout loop or 'foreach' binding for the table body and the text data bindings for the table row data. Now open processes.js, the viewModel and add the jQuery code that makes the PCS REST call as we have done before.

define(['ojs/ojcore', 'knockout', 'jquery'],
 function(oj, ko, $) {
    function ProcessesViewModel() {
      var self = this;
      $.ajax(
        {
          type: "GET",
          url: "http://pcshost:7003/bpm/api/4.0/process-definitions",
          headers: {'Authorization': "Basic d2VibG9naWM6d2VibG9naWMx"},
          contentType: "application/json",
          dataType: "json",
          success: function(json){
             self.proclist = ko.observableArray(json.items);
          },
          failure: function(errMsg) {
             alert(errMsg);
          },
          error: function(xhr){
             alert("An error occured: " + xhr.status + " " + xhr.statusTextt);
          }
        });
    return new ProcessesViewModel();
 }
);

The data binding connection is made by creating a Knockout observableArray from the json.items array returned from the PCS call. self.proclist makes the connection to data-bind="foreach: proclist" <tbody> in the view. Before we test the page lets carry over some CSS from the previous post. Oracle JET makes extensive use of CSS (and SCSS) in the application template and JET components. Our CSS code goes in css/override.css. Open it and insert our header font and color code.

h1, h2, h3, h4 {
    color: DeepSkyBlue;
    text-align: Center;
    font-family: Tahoma, Verdana, Arial;
}
h2, h3, h4 {
    text-align: Left;
}
ul {
    list-style: none;
}

Now test the page by running index.html

Processes-page-test

The table is empty when the page first opens (it is the default page), switch to another page and back to Processes and you should see the table data. This is because the PCS REST call using AJAX is asynchronous and the call is made when the ViewModel is instantiated (constructed) but we don't wait around for the response. Properly refreshing the page data binding after the response comes in is left as an exercise for the reader.

Next we'll do the Instances page. We'll keep the same <ul> list we had last time and bind text values to each item in the list. The HTML code in instances.html looks like

<div class="oj-hybrid-padding">
  <h3>Part 2</h3>
  <p>Retrieve a Process Instance</p>
  <div id="procinstance">
    <h4>Process Instance</h4>
    <ul>
        <li><b>Title:</b> <span data-bind="text: title"></span></li>
        <li><b>ID:</b> <span data-bind="text: processId"></span></li>
        <li><b>Name:</b> <span data-bind="text: name"></span></li>
        <li><b>Owned By:</b> <span data-bind="text: ownedBy"></span></li>
        <li><b>Priority:</b> <span data-bind="text: priority"></span></li>
        <li><b>State:</b> <span data-bind="text: state"></span></li>
    </ul>
  </div>
</div>

and the viewModel in instances.js

define(['ojs/ojcore', 'knockout', 'jquery'],
 function(oj, ko, $) {
    function InstancesViewModel() {
      var self = this;
      $.ajax(
        {
          type: "GET",
          url: "http://pcshost:7003/bpm/api/4.0/processes/10003",
          headers: {'Authorization': "Basic d2VibG9naWM6d2VibG9naWMx"},
          contentType: "application/json",
          dataType: "json",
          success: function(json){
              self.title = ko.observable(json.title);
              self.processId = ko.observable(json.processId);
              self.name = ko.observable(json.processName);
              self.ownedBy = ko.observable(json.ownedBy);
              self.priority = ko.observable(json.priority);
              self.state = ko.observable(json.state);
          },
          failure: function(errMsg) {
             alert(errMsg);
          },
          error: function(xhr){
             alert("An error occured: " + xhr.status + " " + xhr.statusTextt);
          }
        });
    return new InstancesViewModel();
  }
);

Testing the page should look like below. The same asynchronous behavior applies to this page, you will only see response data after the page has been opened more than once.

Instances-page-test

Repeating the View and ViewModel steps for the Tasks page, open tasks.html and add our HTML code as shown

<div class="oj-hybrid-padding">
  <h3>Part 3</h3>
  <p>Retrieve Task List</p>
  <div id="tasklist">
    <h4>Task List</h4>
    <ul data-bind="foreach: tasklist">
      <li>
          <span data-bind="text: title"></span> <b>summary:</b> <span data-bind="text: shortSummary"></span> <b>created:</b> <span data-bind="text: createdDate"></span> - <span data-bind="text: state"></span>
      </li>
    </ul>
  </div>
</div>

Note the <ul> list is data bound with a foreach loop over the elements of the tasklist array returned in the JSON paylod from the PCS task list call. That call is in tasks.js

define(['ojs/ojcore', 'knockout', 'jquery'],
 function(oj, ko, $) {
    function TasksViewModel() {
      var self = this;
      $.ajax(
        {
          type: "GET",
          url: "http://pcshost:7003/bpm/api/4.0/tasks?status=ASSIGNED&assignment=MY_AND_GROUP",
          headers: {'Authorization': "Basic d2VibG9naWM6d2VibG9naWMx"},
          contentType: "application/json",
          dataType: "json",
          success: function(json){
             self.tasklist = ko.observableArray(json.items);
          },
          failure: function(errMsg) {
             alert(errMsg);
          },
          error: function(xhr){
             alert("An error occured: " + xhr.status + " " + xhr.statusTextt);
          }
        });
    return new TasksViewModel();
  }
);

Here the same ko.observableArray(json.items) as we did with the process list payload. The result should look like

Tasks-page-test

Mobile is Easy

Once you've built a web application with Oracle JET building mobile versions for iOS, Android or Windows Mobile is easy. Use the yeoman generator to add hybrid to the JET project. Use command line and change directory to the application project home and enter

yo oraclejet:add-hybrid --platform=ios,android --appid=com.oracle.ateam.pcsrestdemo
grunt serve --platform=ios --device

In the case of iOS you will need to do this on a Mac which has the Apple Xcode developer application installed. In the case of Android, a PC or a Mac with Android Studio installed is required. (I was not able to get yeoman add-hybrid to work with the NetBeans project but it works fine with a plain yeoman generated project. If you are planning on creating mobile variants of your app start with command line yeoman instead of NetBeans until the problem is sorted out.)

Some screen shots from the application above running as is on an iPhone

iPhone page 1

iPhone app menu

iPhone page 2

iPhone page 3

Summary

The PCS REST API makes it easy to incorporate PCS into custom built applications. Using HTML/CSS/JS technology the skills and techniques are portable across a broad spectrum from a simple HTML page to sophisticated applications that use MVVM and MVC application architecture. If you are building complex applications make sure to investigate components in PCS, Vue.js and Oracle JET.

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha