Development tools : XUI editor : Editing an XUI page : Editing the properties of the XUI widgets : Binding data to the XUI widgets
  
Binding data to the XUI widgets
Some of these widgets must have data bound to them. This section describes how to bind data and context to the XUI widgets.
Data can be defined in the global data.xml file, or it can be defined in a self-defined operation file.
For information on the tasks that are involved in binding data and contexts to the XUI widgets, see:
Mapping an XUI file to the data model
Binding data to widgets
See also
Editing the properties of the XUI widgets
Mapping an XUI file to the data model
To enable data to be submitted to a server from the user interface and to enable data to be displayed on the user interface, you must bind your XUI files to the UDTT data model. This topic describes how to bind an XUI file to the UDTT data model.
To bind an XUI file to the UDTT data model, you must specify a context for the XUI file. A context is an object that is used to define and encapsulate data. For example, when a customer enters their name and account number into a banking application, the information is sent to and stored in a context. For more information on contexts, see Contexts.
There are two different types of contexts:
Global context
A global context defines and encapsulates data that can be shared across several application pages. For example, a session context can be used across many pages because it is a common function and not specific to a particular function.
Self-defined context
A self-defined context defines and encapsulates data that cannot be shared across application pages. For example, if there is a deposit page for a banking application, you must create a self-defined context that is related to this page only.
To specify a context for an XUI file
1 In the XUI editor area, click outside the root container.
2 In the Properties view for the XUI editor, click Select context.
The Please select a context window is opens.
This graphic is described in the surrounding text.
3 In the Please select a context window, select the context that you want to bind to the XUI file. Click OK.
See also
Binding data to the XUI widgets
Binding data to widgets
This topic describes general steps to bind data to widgets and gives some specific examples, such as select, combo, tree, table, and fileUpload widget.
Prerequisite
Before you bind data to widgets, you must first define the data in the UDTT Transaction editor: see Defining data in transaction editor.
To bind data to widgets
1 In the Project Explorer view, double-click the XUI file that contains the widgets to which you want to bind data.
The XUI file is displayed in the XUI editor.
2 In the XUI editor area, click the widget to which you want to bind data.
3 In the Properties view of the widget, click the Properties tab.
4 Click in the Value column of the dataName property, and then click Browse.
This graphic is described in the surrounding text.
The Please select a data name window opens.
5 In the right panel, select the context that contains the data that you want to bind to the widget.
6 In the left panel, select the data that you want to bind to the widget. Click OK.
This graphic is described in the surrounding text.
Result
Data is bound to the widget.
See
Binding data to a select widget
Binding data to a Combo widget
Binding data to a tree widget
Binding data to a table widget
Binding data to a fileUpload widget
See also
Binding data to the XUI widgets
Binding data to a select widget
This section provides an example of how to bind data to a select widget.
To define data structure
1 If you get data from context:
You need to specify data structure as following, an IndexedCollection data which includes a KeyedCollection Data, then there are two fields, field labelselect is for lable of select widget, field valueselect is for value of select widget.
Every IndexedCollection data must have at least one kColl data type as a root data container.
This graphic is described in the surrounding text.
Specify the submitted data for the select widget. For example, it can be selectData.selectName.
This graphic is described in the surrounding text.
2 If you get data from list files: You need to create a new javascript file in your project directory /WebContent/listFiles, for example: normal.js, the content is like:
{
'A label' : 'VAL1',
'Another label here' : 'VAL2',
'Label 0001' : 'Value0001',
'Label 0002' : 'Value0002',
'Label 0003' : 'Value0003',
'Label 0004' : 'Value0004',
'Label 0005' : 'Value0005'
}
3 Specify properties for select widget. To get data from list files, specify a value for urlForList; to get data from context data, specify a value for dataNameForList, labelField and valueField. Specify dataName for either of the two modes, which is the submitted value for select widget.
This graphic is described in the surrounding text.
Result
Data is bound to the select widget.
See also
Binding data to widgets
Binding data to a Combo widget
This section provides an example of how to bind data to a Combo widget.
If you get data from context
1 Define Data Structure:
Define an IndexedCollection data to define the data structure of comboBox as following, label is the data to specify combo label.
Every IndexedCollection data must have at least one kColl data type as a root data container.
This graphic is described in the surrounding text.
Specify the submitted data for the comboBox. For example, it can be comboData.comboName.
This graphic is described in the surrounding text.
2 Assign values to dataNameForList and labelField. You need to specify dataName for either of the two modes, which is the submitted value for combo widget.
This graphic is described in the surrounding text.
If you get data from list files
1 Prepare a JavaScript file with proper data structure.
For example: /WebContent/listFiles/normal.js is like:Data in the first column is label field and the second column is value field.
This graphic is described in the surrounding text.
2 Specify the JavaScript file path as the value of urlForList.
In this case, you do not need to edit labelField and valueField since they are defined in the JavaScript file.
This graphic is described in the surrounding text.
Result
Data is bound to the combo widget.
See also
Binding data to widgets
Binding data to a tree widget
This section provides an example of how to bind data to a tree widget.
To bind data to a tree widget
1 Define data structure. Make sure there is “treeModel” definition in btt.xml between <kColl id="classTable"></KColl> in data section.
<field id="treeModel"
value="com.ibm.btt.dojo.model.TreeModel"
description="A treeModel is used and only used to present tree item including label, value, etc, should not be used as generic collection." />
Then you need to define the tree structure model and submit data in operation or flow transaction. For example: add a treeModel type data Tree1RootModel as dataName for Tree content, add a simple filed data Tree1SubmitData as submit dataName for Tree.
This graphic is described in the surrounding text.
Make sure you also define value for label and value for treeModel Tree1RootModel:
This graphic is described in the surrounding text.
After you define treeModel, you need to initialize the treeModel in operation. For example:
public class InitContextOp extends BTTServerOperation {
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
*/
public void execute() throws Exception {
TreeModel root = (TreeModel) getElementAt("Tree1RootModel");
TreeModel level1_1 = new TreeModel("level1-1", "002", "%nls.bttsample/Level1");
TreeModel level2_1 = new TreeModel("level2-1", "005", "Level2");
level1_1.addChild(level2_1);

TreeModel level1_2 = new TreeModel("level1-2", "003", "Level1");
TreeModel level2_2 = new TreeModel("level2-2", "006", "Level2");
level1_2.addChild(level2_2);

TreeModel level1_3 = new TreeModel("level1-3", "004", "Level1");
TreeModel level2_3 = new TreeModel("level2-3", "007", "%nls.bttsample/Level2");
level1_3.addChild(level2_3);

root.addChild(level1_1);
root.addChild(level1_2);
root.addChild(level1_3);

this.fireExitEvent("ok");

}
}
2 Specify properties for tree widget.
In properties view, You need to specify Tree1SubmitData as dataName and Tree1RootModel as dataNameForTreeContent.
This graphic is described in the surrounding text.
Result
Data is bound to the tree widget.
See also
Binding data to widgets
Binding data to a table widget
This section provides an example of how to bind data to a table widget.
To bind data to a table widget
1 Define the data structure.
Define the table column information in a KColl data tableRow, it can be used as submit dataName for single selection mode.
This graphic is described in the surrounding text.
Besides typed data, you could also define a KColl data ColumnData as a child to submit column dataName. Be cautious: This KColl cannot contain any IColl.
Define the list data tableRowIColl which refer to the KeyedCollection data tableRow, it can be used as submit dataname for multiple selection mode.
This graphic is described in the surrounding text.
Define the list data tableData which refer to the KeyedCollection data tableRow, it is the data structure of the whole table data. You can initialize or change the table data in the operation.
This graphic is described in the surrounding text.
Note You could also define a new KColl with the same name and children data as tableRow. Especially, when there is a KColl as child data, this KColl should contain the same children data, too.
2 Specify tableData as dataNameForList.
This graphic is described in the surrounding text.
If selectionMode is single, select tableData as dataName.
If selectionMode is multiple, select tableRowIColl as dataName.
3 Specify Data binding for the column.
Specify ColumnData as Data Name
This graphic is described in the surrounding text.
Advanced data binding properties for the column. According to Editable or not, there are different widgets for selection. You need to specify the related properties for selected widget.
This graphic is described in the surrounding text.
Result
Data is bound to the table widget.
See also
Binding data to widgets
Binding data to a fileUpload widget
This section provides an example of how to bind data to a fileUpload widget.
To define data structure
1 Prepare runtime environment.
Copy commons-fileupload-1.2.2.jar from ${WAS home}\optionalLibraries\Apache\Struts\ to your runtime library and also copy commons-io-1.4.jar to your runtime library.
Make sure following fileHandlers are defined in your btt.xml between tag <kColl id="ajax"></kColl>:
<kColl id="fileHandlers">
  <kColl id="sampleFileHandler">
    <field id="implClass" value="com.ibm.btt.sample.SampleFileHandler" />
    <field id="timeout" value="20000" />
    <field id="maxSize" value="62914560" />
    <field id="cachePath" value="c:\temp\fileupload\cache" />
    <field id="filepath" value="c:\temp\fileupload\upload" />
    <field id="memCacheSize" value="4096" />
  </kColl>
</kColl>
Following source file SampleFileHandler.java is a sample code for your reference; make sure it is ready in package: com.ibm.btt.sample:
package com.ibm.btt.sample;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import com.ibm.btt.base.BTTLog;
import com.ibm.btt.base.BTTLogFactory;
import com.ibm.btt.base.Context;
import com.ibm.btt.base.DSEException;
import com.ibm.btt.base.DSEInvalidArgumentException;
import com.ibm.btt.base.DSEObjectNotFoundException;
import com.ibm.btt.base.KeyedCollection;
import com.ibm.btt.cs.ajax.fileupload.AbstractFileHandler;
import com.ibm.btt.cs.ajax.fileupload.FileUploadUtil;
import com.ibm.btt.sm.BTTSMException;
/**
*
* This class extends HttpServlet class used to handle file updload scenario.
* It will save the uploaded file in file system or configured DB
public class SampleFileHandler extends AbstractFileHandler{

private static final BTTLog LOG = BTTLogFactory.getLog(SampleFileHandler.class.getName());

/**
* name of paramters that will be configured in btt.xml file for this file handler
* make sure the name are exactly the same with the ones in btt.xml file
*/
public static final String PARAM_CACHE_PATH ="cachePath";
public static final String PARAM_FILE_PATH = "filepath";
public static final String PARAM_MEM_CACHE = "memCacheSize";
public static final String PARAM_TIME_OUT = "timeout";
public static final String PARAM_MAX_SIZE = "maxSize";

/**
* cache size, if file is bigger than the size, it will be saved in file system
* or else if will be hold in memory.
*/
protected int memCacheSize = 1024 * 4;
/**
* real system cache folder path
*/
protected String cachePath = null;
/**
* real system upload file folder
*/
protected String filePath = null;
/**
* maximum allowed size of a file
*/
protected long maxSize = 1024 * 1024 * 4;
/**
* fileId maybe a encryped, meaning-less string, maybe a key to
* real file system path of the uploaded file or a key by which the file
* can be accessed from DB.
* In this sample, it just use the file fullpath as the id
*/
private String fileId = null;
/**
* record the current tmp file, it will be used to remove the tmp file
* if the session expired.
*/
private FileItem tmpfile = null;
/**
* name of the file with ext.
*/
private String filename = null;
/**
* initial parameters, this method should be customized based on the handler's implementaion
*
*/
@Override
public void initConfig(KeyedCollection config) throws DSEException {
Enumeration keys = config.getElements().keys();
while(keys.hasMoreElements()){
String key = (String)keys.nextElement();
Object value = config.getValueAt(key);
if(PARAM_CACHE_PATH.equals(key)){
cachePath = value.toString();
}else if(PARAM_FILE_PATH.equals(key)){
filePath = value.toString();
}else if(PARAM_MEM_CACHE.equals(key)){
memCacheSize = Integer.parseInt(value.toString());
}else if(PARAM_TIME_OUT.equals(key)){
this.setTimeout(Long.parseLong(value.toString()));
}else if(PARAM_MAX_SIZE.equals(key)){
maxSize = Long.parseLong(value.toString());
maxSize = maxSize<0 ? -1 : maxSize ;
}
}
// formate file path based on OS, DOS or unix.
this.cachePath = formatFilePath(this.cachePath);
this.filePath = formatFilePath(this.filePath);

StringBuilder sb = new StringBuilder();
if(cachePath==null || cachePath.trim().length()<1){
sb.append("SampleFileHandler : Error, cachePath is null. \n");
}
if(filePath==null || filePath.trim().length()<1){
sb.append("SampleFileHandler : Error, filePath is null. \n");
}
if(memCacheSize<0){
sb.append("SampleFileHandler : Error, memory cache size must bigger than 0 \n");
}
if(getTimeout()<0){
sb.append("SampleFileHandler : Error, timeout value must bigger than 0 \n");
}
if(sb.length()>0){
throw new DSEException(sb.toString());
}
// create relative folders if not existed
initFolders();
}
/**
* Just handle one file upload in this handler, developer can extend to support
* multi-files upload
*/
@Override
public int saveFile(HttpServletRequest request) {
int code = SAVE_FAILED ;

boolean isMultipart = ServletFileUpload.isMultipartContent(request);
if (isMultipart) {
DiskFileItemFactory factory = new DiskFileItemFactory();
//config the memory cache, if above the cache, the file data will be
// saved on cache folder
factory.setSizeThreshold(memCacheSize);
// set up cache folder, when uploading, the tmp file
// will be saved in cache folder with a internal managed name.
factory.setRepository(new File(cachePath));

ServletFileUpload upload = new ServletFileUpload(factory);
// max size of a file
upload.setSizeMax(maxSize);
System.out.println("000maxSize:"+maxSize);

try {
if(isExpired()){
System.out.println("1111timeout!");
return REQ_TIMEOUT ;
}
List<fileItem>fileItems = upload.parseRequest(request);
// save file from request
code = uploadFilesFromReq(request, fileItems);
}catch(SizeLimitExceededException e){
code = FILE_SIZE_EXCEED ;
System.out.println("2222 size exceed!");
} catch (FileUploadException e) {
if(LOG.doDebug()){
LOG.debug("SampleFileHandler:saveFile() -- upload file stream was been cancelled", e);
}
}
}
return code;
}
/**
*
* @param request
* @param fileItems
* @throws IOException
*/
private int uploadFilesFromReq(HttpServletRequest request,
List<FileItem> fileItems){
int code = SAVE_FAILED ;
// get the first file item
FileItem fileItem = getTheFileItem(request,fileItems);
this.tmpfile = fileItem ;
String sessionId = request.getParameter("sessionId");
if(fileItem == null || sessionId == null){
return SAVE_FAILED ;
}
// save file and remove the temp file in cache folder
try {
code = writeFiles(fileItem, request,sessionId);

// remove temp file
fileItem.delete();
this.tmpfile = null;
} catch (IOException e) {
return SAVE_FAILED ;
}
// NOTES: just handle the firest file in the request, and save the first file
// in the file system. developer can extend to support multi-files upload
return code;
}
/**
* NOTES: In this sample, we just handle the first file item in the upload request.
*
* @param request
* @param fileItems upload file items
* @return the first file item or null if errors
*/
private FileItem getTheFileItem(HttpServletRequest request,List<FileItem> fileItems) {
String name="";
for (FileItem fileItem : fileItems) {
//common form item name
name = fileItem.getFieldName();
if (fileItem.isFormField()) {
// common form item value
String value = fileItem.getString();
if(LOG.doDebug()){
LOG.debug(new StringBuilder().append("Request item -- ").append(name).append(": ").append(value).toString());
}
} else {
if (name == null || "".equals(name.trim())) {
continue;
}
return fileItem ;
}
}
return null;
}
/**
* write the files into file sytem. upload files are seperated by sessionId on the
* server side.
* notes that for performance, here we do not use the FileItem.get() method.
*
* @param fileItem
* @param request
* @param sessionId
* @return whether save file success.
* @throws IOException
*/
private int writeFiles(FileItem fileItem, HttpServletRequest request,String sessionId)
throws IOException {
// get the target file folder where to save the uploaded file
// Here use the sessionId to seperate each request files.
StringBuilder spath = new StringBuilder();
spath.append(filePath).append("\\").append(sessionId);
File sFolder = new File(spath.toString());

if (!sFolder.isDirectory()) {
boolean r1 = sFolder.mkdir();
if(r1==false && LOG.doError()){
LOG.error(new StringBuilder().append("Create folder error: ").append(spath).toString());
throw new IOException(new StringBuilder().append("FileUploadServelet: session folder create error").toString());
}
}

// handle the file path for various browsers
String fileNameString = fileItem.getName();
int start = fileNameString.lastIndexOf("\\");
this.filename = fileNameString.substring(start + 1);
// save file
File file = new File(spath.toString(), filename);
byte[] buffer = new byte[1024];
int length = 0;
InputStream is = fileItem.getInputStream();
FileOutputStream fos = new FileOutputStream(file);
while ((length = is.read(buffer)) > 0 && !isExpired()) {
fos.write(buffer, 0, length);
}
// colse stream
fos.close();
is.close();

// remove file if request expired.
if(isExpired() ){
if(file.exists()){
file.delete();
}
return REQ_TIMEOUT ;
}
return SAVE_SUCCESS ;
}
@Override
public int deleteFile(String fileId) {
if(fileId==null || fileId.length()==0){
return DELETE_SUCCESS ;
}
if(isExpired()){
return REQ_TIMEOUT ;
}
File file = retrieveFile(fileId);
boolean r = false;
if(file!=null && file.exists()){
r = file.delete();
this.filename = null ;
}
// clean file Id
this.fileId = null ;

return r ? DELETE_SUCCESS: DELETE_FAILED;
}
@Override
public File retrieveFile(String fileId) {
if(fileId==null || fileId.length()==0 || isExpired()){
return null ;
}
File folder = new File(filePath+"\\"+getSessionId());
if(folder.exists() && folder.isDirectory()){
int value = Integer.parseInt(fileId);
File[] files = folder.listFiles();
for(int i=0; i<files.length; i++){
File file = files[i];
if(value==file.getName().hashCode()){
return file ;
}
}
}
return null;
}
@Override
public String getFileId() {
if(fileId==null && filename!=null){
fileId = new Integer(filename.hashCode()).toString();
}

return fileId ;
}
@Override
public String getFileName(String fileId) {
return this.filename;
}
@Override
public int upldateContext(String fileId) {
if(isExpired()){
return REQ_TIMEOUT ;
}
Context ctx = null;
try {
ctx = FileUploadUtil.getProperContext(getSessionId(), getProcessorId(),getDataname());
} catch (BTTSMException e) {
if(LOG.doError()){
LOG.error("SampleFileHandler : updateContext() -- ", e);
}
}
return doUpdateContext(ctx,fileId);
}

private int doUpdateContext(Context ctx, String fileId) {
int code = UPDATE_CTX_FAILED ;
if(ctx!=null){
try {
KeyedCollection kcoll = (KeyedCollection)ctx.tryGetElementAt(getDataname());
// note that the a kcoll with id=file is mandatory for file upload
//
KeyedCollection fkc = (KeyedCollection)kcoll.tryGetElementAt(FILE);
if(fkc!=null){
//upldated the new file info with the file kcoll.
fkc.setValueAt(FILE_ID, fileId);
fkc.setValueAt(FILE_NAME, this.filename);
File file = this.retrieveFile(fileId);
long size = file == null ? 0 : file.length();
fkc.setValueAt("size", size);

code = UPDATE_CTX_SUCCESS ;

//TODO this can be extend for multi-file upload scenario with a single fileupload
// widget.
// developer can add the existed file info into the iColl with
// id=receivedFiles. and then update the file kcoll with the latest
// uploaded file info
}
} catch (DSEInvalidArgumentException e) {
if(LOG.doError()){
LOG.error("SampleFileHandler: update context error",e);
}
} catch (DSEObjectNotFoundException e) {
if(LOG.doError()){
LOG.error("SampleFileHandler: update context error",e);
}
}
}
return code ;
}
@Override
public int cleanContext() {
return upldateContext("");
}
@Override
protected int doRequestValidation(HttpServletRequest request) {
int size = request.getContentLength();
System.out.println("file size validatoin : fsize = "+size+", max size = "+maxSize);
// notifiy the browser the file size is exceeded.
if(size>maxSize){
return FILE_SIZE_EXCEED ;
}
return REQ_VALID;
}
@Override
public String getFileInfo(int code){
StringBuilder sb = new StringBuilder();
String fId = getFileId();
fId = fId == null ? "": fId ;

String fname = getFileName(fId);
fname = fname == null ? "":fname ;

sb.append("{\"").append(FILE_NAME).append("\":\"")
.append(fname).append("\",\"")
.append(FILE_ID).append("\":\"").append(fId);
if(code!=UPDATE_CTX_SUCCESS){
sb.append("\",\"errorCode\":\"").append(code).append("\"}");
}else{
sb.append("\"}");
}
return sb.toString();
}
/**
* create cache file folders and real file folders
*
*/
private void initFolders() {
File fFolder = new File(filePath);
File tFolder = new File(cachePath);
boolean r1 = true ;
boolean r2 = true ;
if (!fFolder.isDirectory()) {
r1 = fFolder.mkdirs();
if(r1==false && LOG.doError()){
LOG.error(new StringBuilder().append("Create folder error: ").append(filePath).toString());
}
}
if(!tFolder.isDirectory()){
r2 = tFolder.mkdirs();
if(r2==false && LOG.doError()){
LOG.error(new StringBuilder().append("Create folder error: ").append(cachePath).toString());
}
}
}
/**
* format the file path associated with the OS
* @param path
* @return file path associated with the os
*/
private String formatFilePath(String path){
// We test if the entitiesPath is a complete adress for a directory (ex: "d:\er\wee" for DOS, or "/d/er/wee" for UNIX)
char sep = System.getProperty("file.separator").charAt(0);
String p = path.replace('/', sep);
p = path.replace('\\', sep);
if ((p.startsWith(System.getProperty("file.separator")))
|| ((p.length() > 1) && (p.charAt(1) == ':'))) {
return p ;
}
return null;
}
@Override
public AbstractFileHandler clone() {
AbstractFileHandler handler = new SampleFileHandler();
handler.setConfig(getConfig());
handler.setDataname(this.getDataname());
handler.setProcessorId(this.getProcessorId());
handler.setSessionId(this.getSessionId());
try {
handler.initConfig(this.getConfig());
} catch (DSEException e) {
return null ;
}
return handler;
}
/**
*
*/
@Override
public void onRequestExpired(String fileId) {
// delte tmp file if have
if(this.tmpfile!=null){
this.tmpfile.delete();
this.tmpfile = null;
}
// delete file if have
this.deleteFile(getFileId());
// clean ctx if have
this.cleanContext();
}
}
2 Define data structure.
Define following data structure in the transaction data definition, userFiles is the submitted dataname of fileupload widget.
This graphic is described in the surrounding text.
3 Specify properties for fileUpload widget.
Specify dataName as userFiles you defined, for example:
This graphic is described in the surrounding text.
Result
Data is bound to the fileUpload widget.
See also
Binding data to widgets