The initial appearance of the Rich UI application is as follows:

In brief, the data loader requests an initial page; the UI program retrieves the records needed for all pages and returns the total count along with the records for the first page; and the data loader responds to user clicks by invoking the UI program to provide additional pages as needed. When the user requests the last page that was not previously viewed, the data loader requests page number -1, which ends the UI program.
.EglRuiDataGridHeaderCell {
text-align: center;
}
You do not need to deploy the Rich UI application to the target project until all development is complete.
To make generation work, specify that the genProject build descriptor option refers to the target project. Also ensure that the deployment descriptor has an entry for deploying the gateway REST service and that you check the Web Service Deployment tab, Stateful checkbox.
http://localhost:8080/MyTargetProject/MyRichUIHandler-en_US.html
That address is appropriate in the following case: you deploy the project on your workstation, the application server is listening on port 8080, the target project is named MyTargetProject, the Rich UI handler is named MyRichUIHandler, and the HTML file is configured for the US English locale.
The database schema is appropriate only for a sample. For example, no key constraints are present.
CREATE TABLE APP.CAR_MASTER
(
VIN CHAR(17) NOT NULL,
Make VARCHAR(15) NOT NULL,
Model VARCHAR(15) NOT NULL,
TheYear CHAR(4) NOT NULL
);
CREATE TABLE APP.CAR_PURCHASES
(
VIN CHAR(17) NOT NULL,
PURCHASE_DATE DATE NOT NULL,
COST DECIMAL(8,2) NOT NULL
);
CREATE TABLE APP.CAR_SALES
(
VIN CHAR(17) NOT NULL,
SALE_DATE DATE NOT NULL,
PRICE DECIMAL(8,2) NOT NULL
);
INSERT INTO APP.CAR_MASTER VALUES
( '11111111111111111', 'Honda', 'Accord', '1998'),
( '22222222222222222', 'Ford', 'Mustang', '2006'),
( '33333333333333333', 'Chevrolet', 'Camaro', '2010'),
( '44444444444444444', 'Toyota', 'RAV4', '2008'),
( '55555555555555555', 'Triumph', 'Spitfire', '1980'),
( '66666666666666666', 'BMW', '328XI', '2007'),
( '77777777777777777', 'Cadillac', 'Escalade', '2004'),
( '88888888888888888', 'Chrysler', 'Sebring', '2006'),
( '99999999999999999', 'Lexus', 'ES 300', '2009'),
( 'AAAAAAAAAAAAAAAAA', 'Honda', 'Civic', '2008'),
( 'BBBBBBBBBBBBBBBBB', 'Toyota', 'Celica', '2005');
INSERT INTO APP.CAR_PURCHASES VALUES
( '11111111111111111', '2004-06-15', 4000.00),
( '22222222222222222', '2008-07-26', 7200.00),
( '33333333333333333', '2010-11-11', 21000.00),
( '44444444444444444', '2009-02-02', 18000.00),
( '55555555555555555', '2007-01-04', 20500.00),
( '66666666666666666', '2010-08-08', 26900.00),
( '77777777777777777', '2010-04-04', 17200.00),
( '88888888888888888', '2009-11-06', 11400.00),
( '99999999999999999', '2009-12-07', 37000.00),
( 'AAAAAAAAAAAAAAAAA', '2010-11-12', 13000.00),
( 'BBBBBBBBBBBBBBBBB', '2008-03-03', 8300.00);
INSERT INTO APP.CAR_SALES VALUES
( '11111111111111111', '2004-11-18', 4800.00),
( '22222222222222222', '2009-01-12', 7000.00),
( '33333333333333333', '2010-11-14', 22000.00),
( '44444444444444444', '2009-02-03', 19200.00),
( '55555555555555555', '2007-02-18', 23500.00),
( '66666666666666666', '2010-09-20', 28000.00),
( '77777777777777777', '2010-05-06', 18500.00),
( '88888888888888888', '2009-11-06', 11400.00),
( '99999999999999999', '2010-02-07', 37200.00),
( 'AAAAAAAAAAAAAAAAA', '2010-11-16', 13800.00),
( 'BBBBBBBBBBBBBBBBB', '2008-04-05', 7400.00);
Here is the example UI program:
package myPkg;
record CarStatus type SQLRecord {
tableNames =[["APP.CAR_MASTER", "A"],
["APP.CAR_PURCHASES", "B"],["APP.CAR_SALES", "C"]],
defaultSelectCondition =
#sqlCondition{ A.VIN = B.VIN AND A.VIN = C.VIN ORDER BY VIN},
keyItems =[VIN]}
VIN string{column = "A.VIN"};
MAKE string;
MODEL string;
YEAR string{column = "A.THEYEAR"};
PURCHASE_DATE date;
COST decimal(8, 2);
SALE_DATE date;
PRICE decimal(8, 2);
end
// initial page size from requester
Record PageSizeContainer
pageSize INT;
end
Record MyException type Exception end
Record SendListContainer
// number of available records
numberOfRecords INT;
pageNumber INT = 1;
sendList CarStatus[]{};
end
program MyUIProgram type UIProgram
{ inputUIRecord = myPageSizeContainer, segmented = true }
const MAXIMUM_DATABASE_ROWS INT = 100;
myPageSizeContainer PageSizeContainer;
function main()
tentativeCarListSize INT;
numberToSend INT;
numberOnLastPage INT;
sendCounter, startRowOnPage, endRowOnPage INT;
numberOfAvailablePages INT;
mySendListContainer SendListContainer;
myCarList CarStatus[]{};
i int;
if (myPageSizeContainer.pageSize <= 0)
throw new MyException{ message =
"Your page size is not valid. You requested " + numberToSend + "."};
end
tentativeCarListSize = getNumberofCars();
if (tentativeCarListSize > MAXIMUM_DATABASE_ROWS || tentativeCarListSize == 0)
throw new MyException{message =
"The number of available rows is not valid. Cannot return " +
tentativeCarListSize + " rows."};
end
// set up array with retrieved database rows.
try
get myCarList;
SQLLib.disconnect();
onException(except AnyException)
throw new MyException{message = "Problem in data access. "
+ except.message};
end
// no exception thrown if too many rows now
mySendListContainer.numberOfRecords = myCarList.getSize();
numberOfAvailablePages =
MathLib.Ceiling(mySendListContainer.numberOfRecords /
myPageSizeContainer.pageSize);
numberOnLastPage = mySendListContainer.numberOfRecords %
myPageSizeContainer.pageSize;
if (numberOnLastPage == 0)
numberOnLastPage = myPageSizeContainer.pageSize;
end
while (mySendListContainer.pageNumber != -1)
// set up array for the number of elements to send
if (mySendListContainer.pageNumber < numberOfAvailablePages)
numberToSend = myPageSizeContainer.pageSize;
else
numberToSend = numberOnLastPage; // last page
end
for (sendCounter from 1 to numberToSend)
mySendListContainer.sendList.appendElement(new CarStatus{});
end
// specify which database records to send
if (mySendListContainer.pageNumber == 1)
startRowOnPage = 1;
else
startRowOnPage = (mySendListContainer.pageNumber - 1) *
myPageSizeContainer.pageSize + 1;
end
if (mySendListContainer.pageNumber == numberOfAvailablePages )
endRowOnPage = startRowOnPage + numberOnLastPage - 1;
else
endRowOnPage = startRowOnPage + myPageSizeContainer.pageSize - 1;
end
// copy the database records to send
i = startRowOnPage;
for (n int from 1 to numberToSend)
mySendListContainer.sendList[n] = myCarList[i];
i = i + 1;
end
converse mySendListContainer;
mySendListContainer.sendList.removeAll();
end
end // end main()
function getNumberOfCars() returns(int)
numberOfRows INT;
countQuery STRING = "Select count(*) from APP.CAR_MASTER";
try
prepare myTest from countQuery;
get with myTest into numberOfRows;
onException(except AnyException)
throw new MyException{message =
"Problem in accessing the row count. " + except.message}; end
return(numberOfRows);
end
end
Here is the example Rich UI application:
package myRichUIPkg;
import com.ibm.egl.rui.widgets.DataGrid;
import com.ibm.egl.rui.widgets.DataGridColumn;
import com.ibm.egl.rui.widgets.DataGridLib;
import egl.ui.gateway.UIGatewayRecord;
record CarStatusBasic type SQLRecord
VIN string;
MAKE string;
MODEL string;
YEAR string;
PURCHASE_DATE date;
COST decimal(8, 2);
SALE_DATE date;
PRICE decimal(8, 2);
end
record PageSizeContainer
pageSize int;
end
// field names must match entries in the JSON string
record CarStatusBasicContainer
numberOfRecords int;
pageRequested int{JSONName = "pageNumber"} = 1;
sendList CarStatusBasic[]{};
end
handler MyRichUIHandler type RUIhandler
{initialUI =[grid], onConstructionFunction = start,
cssFile = "css/RichUIProject.css", title = "View Car Sales"}
grid DataGrid{pageSize = PAGE_SIZE, showButtonBar = true, style = "overflow:auto",
columns =[
new DataGridColumn{name = "VIN", displayName = "VIN", width = 135,
alignment = DataGridLib.ALIGN_CENTER},
new DataGridColumn{name = "MAKE", displayName = "Make",
alignment = DataGridLib.ALIGN_CENTER},
new DataGridColumn{name = "Model", displayName = "Model",
alignment = DataGridLib.ALIGN_CENTER},
new DataGridColumn{name = "Year", displayName = "Year",
alignment = DataGridLib.ALIGN_CENTER},
new DataGridColumn{name = "Purchase_Date", displayName = "Purchase Date",
alignment = DataGridLib.ALIGN_CENTER},
new DataGridColumn{name = "Cost", displayName = "Purchase Price",
alignment = DataGridLib.ALIGN_RIGHT, width=90},
new DataGridColumn{name = "Sale_Date", displayName = "Sale Date",
alignment = DataGridLib.ALIGN_CENTER},
new DataGridColumn{name = "Price", displayName = "Sale Price",
alignment = DataGridLib.ALIGN_RIGHT, width =80},
new DataGridColumn{name = "Profit", displayName = "Profit (Loss)",
formatters =[formatProfit], alignment = DataGridLib.ALIGN_RIGHT, width = 80}]};
const PAGE_SIZE int = 5;
myPageSizeContainer PageSizeContainer;
listContainer CarStatusBasicContainer{};
firstInvocation boolean;
gridDataList CarStatusBasic[1]{};
requestedPage int = 1;
pagesRequested boolean[]{};
handledEarlier boolean;
numberOfAvailablePages int;
gateRec UIGatewayRecord{};
allLoaded boolean;
gatewayServiceVar UIGatewayService{@BindService{bindingKey = "UIGatewayService"}};
function start()
gateRec.uiProgramName = "myPkg.MyUIProgram";
myPageSizeContainer.pageSize = PAGE_SIZE;
StrLib.defaultDateFormat = "yyyy-MM-dd";
pagesRequested.appendElement(false);
firstInvocation = true;
allLoaded = false;
// at this writing, sorting is available only when all the data is loaded
grid.enableSorting = false;
grid.dataLoader = myDataLoader;
grid.data = gridDataList as any[];
end
function formatProfit(class string inOut, value string inOut, rowData any in)
calculation decimal(8, 2) =
rowData.Price as decimal(8, 2) - rowData.Cost as decimal(8, 2);
if(calculation < 0)
calculation = -calculation;
value = "(" + calculation + ")";
else
value = calculation as string;
end
end
function myDataLoader(startRow int in, endRow int in, sortFieldName string in,
sortDirection int in) returns(boolean)
if(allLoaded)
return(true);
end
if(firstInvocation)
if(pagesRequested[1] == true)
handledEarlier = true;
else
handledEarlier = false;
pagesRequested[1] = true;
gateRec.data = ServiceLib.convertToJSON(myPageSizeContainer);
call gatewayServiceVar.invokeProgram(gateRec)
returning to callbackFunc onException handleException;
end
else
requestedPage = mathLib.ceiling(startRow /
myPageSizeContainer.pageSize);
if(pagesRequested[requestedPage] == true)
handledEarlier = true;
else
handledEarlier = false;
listContainer.pageRequested = requestedPage;
pagesRequested[requestedPage] = true;
gateRec.data = ServiceLib.convertToJSON(listContainer);
call gatewayServiceVar.invokeProgram(gateRec)
returning to callbackFunc onException handleException;
end
end
return(handledEarlier);
end
function callbackFunc(gatewayRecord uigatewayrecord in)
i, n, startUpdate, endUpdate int;
ServiceLib.convertFromJSON(gatewayRecord.data, listContainer);
numberOfReturns int = listContainer.sendList.getSize();
allLoaded = true;
// can set up the pagesRequested array only after learning
// the number of records available from the UI program
if(firstInvocation)
numberOfAvailablePages =
MathLib.Ceiling(listContainer.numberOfRecords /
myPageSizeContainer.pageSize);
if(numberOfAvailablePages > 1)
for(i from 2 to numberOfAvailablePages)
pagesRequested.appendElement(false);
end
end
// set up the data array for the grid
for(i from 2 to listContainer.numberOfRecords)
GridDataList.appendElement(new CarStatusBasic);
end
firstInvocation = false;
end
// end use of the UI program?
for(i from 1 to numberOfAvailablePages)
if(pagesRequested[i] == false)
allLoaded = false;
end
end
if(allLoaded)
grid.enableSorting = true;
listContainer.pageRequested = -1;
gatewayRecord.data = ServiceLib.convertToJSON(listContainer);
call gatewayServiceVar.invokeProgram(gatewayRecord)
returning to handleServiceEnd onException handleException;
end
// load data
if(requestedPage == 1)
for(i from 1 to numberOfReturns)
gridDataList[i] = listContainer.sendList[i];
end
else
startUpdate = (requestedPage - 1) * myPageSizeContainer.pageSize + 1;
endUpdate = startUpdate + numberOfReturns - 1;
n = 1;
for(i from startUpdate to endUpdate)
gridDataList[i] = listContainer.sendList[n];
n = n + 1;
end
end
grid.data = gridDataList as any[];
grid.render();
end
function handleException(exp AnyException in)
SysLib.writeStdOut(exp.message);
grid.cancelDataLoader();
end
function handleServiceEnd(gateRec uiGatewayRecord in)
if(!gateRec.terminated)
SysLib.writeStdOut("Error: The program is still running.");
grid.render();
end
end
end