Master - Detail synchronization in an Oracle JET rich client application against a REST API image 163

Master – Detail synchronization in an Oracle JET rich client application against a REST API

In a recent article (Extend Oracle JET with Table on REST API and Deploy to Node.js in Application Container Cloud) , I have described how I created a data bound Oracle JET application with a rich JET table component that reads its data from a REST API (that gets it from a DBaaS instance in its turn). The application developed in that article shows a table with Departments – read from the HR database schema. A nice next step it seems is to show a list of employees as well – and only the employees that work in the currently selected department in the departments table. When a new department is selected, the list of employees should be refreshed to align with the selected department.

The application I am creating will look as follows – after deployment onto the Application Container Cloud that is:

image

 

The steps for extending the application with a single table – Departments – to support a synchronized list of Employees are:

  • Add table component to the hrm.tmpl.html template file
  • Add support for single row selection to the existing table component for departments; also specify an event listener to consume the row-selection event
  • Define the Employee model and Employees Collection in the hrm.js file that defines the view model for the hrm.tmpl.html template in a similar way as the existing Department Model and Departments Collection (note: the data is fetched from a URL that contains a department identifier; this is will provide the linking pin for the synchronization between the departments table (with the selected row) and the employees table
  • Implement the table event listener: it has to find the newly selected department row in the table and then refresh the employees collection from the REST API, referencing the URL with the new value for the department identifier
  • Because we need to use jQuery to find the currently selected row in the departments table, we also need to configure hrm.js for using jQuery and the jQuery $ object

It took me a few hours (!) to get it all to work (I am still quite new to JET and I can not yet easily find the supporting resources). In the end, the number of code lines required to make this work is very small, as you will see in the next few sections.

Modifying the HRM Template

Two things need to happen in this template (hrm.tmpl.html): add the table component for the employees table and add selection support to the departments table.

 

<div id="deptList" class="oj-md-9 oj-col"></div>
<div id="empList" class="oj-md-9 oj-col"></div>

Modifying the View Model for the HRM Template

Slightly more complex changes are applied to the file hrm.js (in public\js\modules).

The Employee Model and Collection are defined to support the Employee table, in a similar vein as was already done for the Departments. However, because we will need to refresh the Employees collection whenever a new department is selected, I have created a function to perform that data refresh: refreshEmployees(). This function takes a Department Id as its input and then takes care of retrieving the data from the REST API and initializing the employees data source in the view model to which the table component is bound.

Property selectionListener in the hrm view model is set to a function that implements the interface required for a table event handler (two input parameters). The function determines the identifier of the table on which the event happened (although of course we know it is dept-table and could have hard coded that, it is seems nice to have it more dynamic). It then uses a little function – with complex logic and direct use of jQuery! – to find the row key (or department identifier) for the newly selected  row in the table. This key is passed to function refreshEmployees() that subsequently takes care of fetching the relevant employee data and resetting the Employees table – because self.empDatasource is defined as a two way bound Knockout Observable.

 

 

Support for the Employee Collection – retrieved from the REST API

self.EmpCol = ko.observable(); // EmpCol is an observable so when its data is in (from the REST service), the datasource is triggered
self.empDatasource = ko.observable(); // datasource is an observable so when it is triggered/refreshed, the table component is triggered

function parseRESTDBEmployee(response) {
return {EmployeeId: response['EMPLOYEE_ID'], Name: response['FIRST_NAME']+" "+response['LAST_NAME'], Job: response['JOB_ID']};
}

// think of this as a single record in the DB, or a single row in your table
var Employee = oj.Model.extend({
parse: parseRESTDBEmployee,
idAttribute: 'EmployeeId'
});

// this defines our collection and what models it will hold
var EmpCollection = oj.Collection.extend({
model: new Employee(),
comparator: "EmployeeId"
});

self.refreshEmployees = function (departmentId) {
if (departmentId !=null) {
self.EmpCol(new EmpCollection());
self.EmpCol().url = self.serviceURL+"/departments/"+departmentId;
self.empDatasource(new oj.CollectionTableDataSource(self.EmpCol()));
} else {
self.empDatasource(null);
}
}

The event listeners (note: only the selection event is truly used here)

...
// resource: https://blogs.oracle.com/geertjan/entry/selecting_a_row_in_oracle1
self.currentRowListener = function (event, data) { console.log('before current row listener'); };
self.selectionListener = function (event, data)
{ var tableId = data.currentTarget.id;
var key = currentSelection(tableId);
event.refreshEmployees(key);
};
self.sortListener = function (event, data) {console.log('sort listener');};
}

function myselectionListener(event, data) // event contains a reference to the HRM viewmodel
{ var obj = data.currentTarget;
var tableId = obj.id;
var key = currentSelection(tableId);
event.refreshEmployees(key);
}

// find the currently selected key - using the knowledge that single row selection is configured on the table
function currentSelection(tableId)
{
var selectionObj = $("#"+tableId).ojTable("option", "selection");
return selectionObj[0]?selectionObj[0].startKey.row:null;
};

to support use of jQuery

define(['ojs/ojcore', 'knockout', 'jquery','ojs/ojknockout','ojs/ojmodel', 'promise', 'ojs/ojtable'],

function(oj, ko,$)
{
function hrmViewModel() {
...

Running the application

This is what it looks like. Initially, no data is loaded at all:

image

Then, the departments are loaded, but none is selected yet:

image

Then, a department is selected (department 90). The corresponding set of employees is fetched and displayed:

image

Another department is selected:

image

and finally one without employees:

image

Whether I have found the best possible way to synchronize the Employees collection with the events that happen around the Departments table, I cannot really tell. However, I am quite happy with the end result. Suggestions for improvement are very much welcome.

 

Resources

Crucial article by Geertjan Wielenga on tapping into the table events: Selecting a Row in Oracle JET ojTable (Part 2) .

JET Cookbook on Table component (and events).

Download the zip file with the Oracle JET application (minus the 35 MB libraries from folder public/js/libs): oraclejetwithrest_onnode.zip.

One Response

  1. printer keeps going offline October 27, 2018