Implement a Lazy-Loading Table in a Report (on IBM BPM v7.5)
Objective: Implement a Lazy-Loading Table in a Report (on IBM BPM v7.5).
Steps:
Step 1. Supporting Work
Step 2. Data Store Implementation
Step 3. Create Tables
Step 1. Supporting Work
We're going to be calling a Service to get each page of data for our Table. The out-the-box client-side Javascript only provisions for asynchronous calls but our approach requires a synchronous call. We can create our own synchronous versions of the out-the-box functions...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* This is a synchronous version of "callService()" that we can use in our Lazy-Loading Data Grids. */ | |
tw.coach.callSynchronousService = function (service, params, snapshot) { | |
if (snapshot == "" || snapshot == null) { | |
snapshot = coachSnapshotContext; | |
} | |
var url = "/portal/jsp/callService.do?serviceName=" + service + "&snapshotId=" + snapshot; | |
return callSynchronousServiceImpl(url, params); | |
}; | |
/* This is a synchronous version of "callServiceImpl()" that we can use in our Lazy-Loading Data Grids. | |
The "false" in "http_request.open()" is what makes it synchronous. */ | |
function callSynchronousServiceImpl(url, params) { | |
var parameters = "input=" + encodeURIComponent(params); | |
var http_request = null; | |
if (window.XMLHttpRequest) { // Mozilla, Safari,... | |
http_request = new XMLHttpRequest(); | |
if (http_request.overrideMimeType) { | |
http_request.overrideMimeType('text/xml'); | |
} | |
} else if (window.ActiveXObject) { // IE | |
try { | |
http_request = new ActiveXObject("Msxml2.XMLHTTP"); | |
} catch (e) { | |
try { | |
http_request = new ActiveXObject("Microsoft.XMLHTTP"); | |
} catch (e) {} | |
} | |
} | |
if (!http_request) { | |
alert('Cannot create XMLHTTP instance'); | |
return false; | |
} | |
http_request.open('POST', url, false); | |
http_request.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); | |
// The browser just complains about these so I have commented them out... | |
// http_request.setRequestHeader("Content-length", parameters.length); | |
// http_request.setRequestHeader("Connection", "close"); | |
http_request.send(parameters); | |
var ret = new Object(); | |
if (http_request.status == 200) { | |
var result = http_request.responseXML.getElementsByTagName("output"); | |
if (window.XMLHttpRequest && window.ActiveXObject) { // necessary for IE7 | |
http_request.responseXML.load(http_request.responseBody); | |
result = http_request.responseXML.getElementsByTagName("output"); | |
} | |
for (var i = 0; i < result.length; i++) { | |
var output = result[i]; | |
var name = output.getAttribute("name"); | |
ret[name] = parseValues(output.firstChild, ret); | |
} | |
} | |
return ret; | |
} |
Step 2. Data Store Implementation
Dojo provides many Data Stores but, unfortunately, none of them quite fit what we want to do. Fortunately, it's quite easy to extend the "QueryReadStore" (in particular the "_fetchItems()" function) to give us what we want...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
dojo.require("dojox.data.QueryReadStore"); | |
dojo.provide("bt.LazyLoadStore"); | |
dojo.declare("bt.LazyLoadStore", dojox.data.QueryReadStore, { | |
lazyLoadFunction: null, | |
constructor: function(args) { | |
dojo.safeMixin(this,args); | |
}, | |
_fetchItems: function(request, fetchHandler, errorHandler) { | |
var serverQuery = request.serverQuery || request.query || {}; | |
if(!this.doClientPaging) { | |
serverQuery.start = request.start || 0; | |
if (request.count) { | |
serverQuery.count = request.count; | |
} | |
} | |
if(!this.doClientSorting) { | |
if(request.sort) { | |
var sort = request.sort[0]; | |
if(sort && sort.attribute) { | |
var sortStr = sort.attribute; | |
if(sort.descending) { | |
sortStr = "-" + sortStr; | |
} | |
serverQuery.sort = sortStr; | |
} | |
} | |
} | |
if(this.doClientPaging && this._lastServerQuery!==null && dojo.toJson(serverQuery)==dojo.toJson(this._lastServerQuery)) { | |
this._numRows = (this._numRows === -1) ? this._items.length : this._numRows; | |
fetchHandler(this._items, request, this._numRows); | |
} | |
else { | |
var returnData = this.lazyLoadFunction(request); | |
this._xhrFetchHandler(returnData, request, fetchHandler, errorHandler); | |
this.lastRequestHash = new Date().getTime()+"-"+String(Math.random()).substring(2); | |
this._lastServerQuery = dojo.mixin({}, serverQuery); | |
} | |
} | |
}); |
Note that the function to actually do the lazy-loading is a parameter. This means that we can easily re-use our Data Store. Here's an example of a lazy-loading function...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function lazyLoadMyData(request) { | |
console.log("lazyLoadMyData(): " + JSON.stringify(request.query)); | |
var inputs; | |
inputs = "<inputs>" | |
+ "<variable name ='filter'>" + request.query.myFilter + "</variable>" | |
+ "<variable name ='start'>" + request.start + "</variable>" | |
+ "<variable name ='count'>" + request.count + "</variable>" | |
+ "<variable name ='sortColumn'>" + request.sort[0].attribute + "</variable>" | |
+ "<variable name ='sortOrder'>" + request.sort[0].descending + "</variable>" | |
+ "</inputs>"; | |
var returnData = {}; | |
var data = tw.coach.callSynchronousService("Lazy Load My Data", inputs, snapShotId); | |
returnData.items = JSON.parse(data.myData).items; | |
if (returnData.items.length > 0) { | |
returnData.numRows = returnData.items[0].rowCount; | |
} | |
else { | |
returnData.numRows = 0; | |
} | |
returnData.identifier = 'column1'; | |
return returnData; | |
} |
Note 1: The "snapShotId" is set on the report using something like this...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var snapShotId = '<#= tw.system.model.processAppSnapshot.id #>'; |
Note 2: The total number of rows is included as a column in the result set. It could easily be a separate parameter.
Step 3. Create Tables
Now we're ready to create the DataGrids. You can write your own code to do this but here's mine...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function createLazyGrid(containerId, tableStructure, dojoDataStore, queryFilter) { | |
var gridParameters = { | |
structure: tableStructure, | |
selectable: true, | |
store: dojoDataStore, | |
escapeHTMLInData: false, | |
autoRender: true, | |
autoWidth: true, | |
autoHeight: true, | |
rowsPerPage: 20, | |
query: queryFilter, | |
plugins:{ | |
indirectSelection: true, | |
pagination:{pageSizes:false, description:true, pageStepper:true, gotoButton:true, maxPageStep:4, position:"bottom", defaultPage:20} | |
} | |
}; | |
var lazyGrid = new dojox.grid.EnhancedGrid(gridParameters); | |
lazyGrid.placeAt(containerId); | |
lazyGrid.startup(); | |
return lazyGrid; | |
} | |
function drawMyTable() { | |
dojo.empty("myTableDiv"); | |
var tableStructure = [[ | |
{field: "column1", name: "Column 1",width:"100px"}, | |
{field: "column2" , name: "Column 2",width:"100px"}, | |
{field: "column3", name: "Column 3",width:"100px"} | |
]]; | |
var dojoDataStore = new bt.LazyLoadStore({lazyLoadFunction: lazyLoadMyData}); | |
paUnAllocatedGrid = createLazyGrid('myTableDiv', tableStructure, dojoDataStore, {column1:'*'}); | |
} |