Hi,
I was on an XPages course and it covered how to add multi values to a form using a repeat control. I am trying to change this so I can add 3 attachments per row. When the user clicks ‘Add’ it will add another row where they can attach another 3 files.
The problem I am having, is that its clearing the filepaths I put into the previous rows, so I do get another row, but I need to browse for all the files again (on all rows).
It uses a Javascript Script, so I dont really know where its going wrong. The example had the data binding going to “fieldGroup[0]”, “fieldGroup[1]”, “fieldGroup[2]”. I have tried to change the upload controls to these values, but it still clears them. I have also tried “Attach[0]”,“Attach[1]”,“Attach[2]”, but this doesn’t even load the page.
Here is the Javascript code :
/**
-
The table walker helps to manage collections of multi-value fields
-
that form a table inside a notes document
**/
var tableWalker = {
variableName : "variable",
dataName : "data",
fieldName : "fields",
fieldCount : 0,
fieldArray : new Array(),
// Set the variable names for the variables we use in the context
// We might get away without them once we can make sure we have stable JSON storage
setVariableNames : function(vname) {
this.variableName = vname;
this.dataName = vname+"_data";
this.fieldName = vname+"_fields";
},
// The fields for this datasource
setFieldList : function(fList:String) {
if (fList == null || fList == "") {
this.fieldList = viewScope.get(this.fieldName)
} else {
// We overwrite the field list
viewScope.put(this.fieldName,fList);
this.fieldList = fList
}
this.fieldArray = this.fieldList.split(",");
this.fieldCount = this.fieldArray.length
},
/**
* getDataSource retrieves a collection of multivalue fields as datasource
* If the variable already exists in the viewScope that variable is used
* otherwise the field values are pulled from the given Domino document
* doc: the NotesDocument
* fieldList: a string with all the fieldName
* variableName: under wich name will that field be stored into the viewScope
* there will be 2 variables one [name]_data and one [name]_fields
* e.g. if variableName is abc the data is abc_data and the fieldnames is abc_fields
* if the caller is sure, that the source exists only the variable name is needed
* same applies if the caller is sure that the fieldList is already in the scope
*
**/
getDataSource : function (variableName:String, doc:NotesXspDocument, fieldList:String) {
// Capture the variables
this.setVariableNames(variableName);
// Now we need to retrieve the field names
this.setFieldList(fieldList)
// Try to get valid data from the viewScope
var theDataWeWant:Array = viewScope.get(this.dataName);
if ( theDataWeWant != null) {
// We have existing content and are done
return theDataWeWant
}
// Now we check if we have a valid document, we would need that
if (doc == null || typeof doc === "undefined") {
print("Document was null");
return null //We don't have data here
}
// And the data... using an existing function
return this.storeDataSourceInViewScope(doc)
},
/**
* storeDataSourceInViewScope extracts a set of multi value fields into an array and stores that into
* the viewContext for later retrieval
* doc: the NotesDocument
* fieldList: a string with all the fieldName
* variableName: under wich name will that field be stored into the viewScope
* there will be 2 variables one [name]_data and one [name]_fields
* e.g. if variableName is abc the data is abc_data and the fieldnames is abc_fields
**/
storeDataSourceInViewScope : function(doc:NotesXspDocument) {
var theDataWeWant = new Array(); // Our matrix with field values
// get a 2 dimensional array with rows=fields, cols=field values
var fieldValueArray:Array = this.getListWithValues(doc);
var firstFieldSet = fieldValueArray[0]; // The first column, we use to determine row count
var rowCount = firstFieldSet.length; // How many rows with values
var colCount = this.fieldCount; // How many unique fields = columns
// now transpose that array to cols = fields and rows= field values
for (var row = 0; row < rowCount; row++) {
// Array that holds one value row
var rowWithValues = new Array();
// Loop through all properties a.k.a columns
for (var col = 0; col < colCount; col++) {
// remember: in the source array it row and col are mixed up
rowWithValues.push(fieldValueArray[col][row]);
}
// Assign the new Object to the result
theDataWeWant.push(rowWithValues);
}
// Store our results into the viewContext
viewScope.put(this.dataName,theDataWeWant);
// Return the array for direct use
return theDataWeWant
},
// Retrieves a list of field values into an array, one element (row) per field, the
// elements are arrays (columns) of values
getListWithValues : function(doc:NotesXspDocument) {
var result = new Array(); // the resulting array
for (var i = 0; i < this.fieldCount; i++) {
// NotesItem values come back as Vectors
var rawResult = doc.getValue(this.fieldArray[i]);
var curResult = new Array();
// Loop through the resulting values, works for array and vector and is
// neutral to the data type
if (rawResult != null) {
for(curVal in rawResult) {
curResult.push(curVal)
}
}
// This adds one field
result.push(curResult);
}
return result
},
/**
*
* addRow adds a row to an existing array that is bound to controls
* variableName = name of viewScope variable to hold the Array
* rowNumberToInsertBefore = where to insert, if omitted -> append a row
**/
addRow : function (variableName:String, rowNumberToInsertBefore) {
//Get the current array
var theDataWeWant:Array = this.getDataSource(variableName);
if (theDataWeWant == null) {
//Nothing to add on
print("Function addRow: DataArray is empty")
return null
}
var emptyArray = new Array(this.fieldCount);
//Now append that to the existing array
if (rowNumberToInsertBefore == null) {
theDataWeWant.push(emptyArray)
} else {
//Splice doesn't seem to work with with two dimensional arrays
//theDataWeWant.splice(rowNumberToInsertBefore,0,emptyArray)
var curRowCount = theDataWeWant.length; // How much data do we have
var insertComplete = false; // To handle too high insert rows
var newDataWeWant = new Array();
// Move all rows to new array. Insert where appropriate
for (var i = 0 ; i < curRowCount; i++) {
if (rowNumberToInsertBefore == i && !insertComplete) {
newDataWeWant.push(emptyArray);
insertComplete = true
}
newDataWeWant.push(theDataWeWant[i])
}
if (!insertComplete) {
// We don't have a new line, so we append one
newDataWeWant.push(emptyArray);
}
// Store it back
viewScope.put(this.dataName,newDataWeWant);
return newDataWeWant
}
return theDataWeWant
},
/**
*
* removeRow adds a row to an existing array that is bound to controls
* variableName = name of viewScope variable to hold the Array
* rowNumberToDelete = which row needs deletion
**/
removeRow : function (variableName:String, rowNumberToDelete) {
var theDataWeWant:Array = this.getDataSource(variableName);
if (theDataWeWant == null) {
//Nothing to add on
print("Function removeRow: DataArray is empty")
return null
}
var curRowCount = theDataWeWant.length;
if (curRowCount > 1) {
// We only proceed if we have more than one row left
// Splice doesn't seem to work with 2 dimensional arrays
// theDataWeWant.splice(rowNumberToDelete,1)
if (rowNumberToDelete == (curRowCount - 1)) {
theDataWeWant.pop() //Last row, just pop it away
} else {
// We need to rescue the stack
var newDataWeWant = new Array();
// Move all rows to the rescue array
for (var i = 0 ; i < curRowCount; i++) {
if (!(rowNumberToDelete == i)) {
newDataWeWant.push(theDataWeWant[i])
}
}
// Store it back
viewScope.put(this.dataName,newDataWeWant);
return newDataWeWant
}
}
return theDataWeWant
},
/**
*
* updateDocument writes the field value back into the Notes document
* typically in a QueryDocumentSave event
*
*/
updateDocument : function updateDocument(variableName:String, doc:NotesXspDocument) {
var fieldData:Array = this.getDataSource(variableName);
if ( fieldData == null) {
// No data in the array, nothing to do
print("No fieldData found in updateDocument");
return null
}
// We need to pick for every field the column value from the array
// TODO: make this more efficient!
for (var i = 0; i < this.fieldArray.length; i++) {
var curFieldName = this.fieldArray[i];
var curData = new Array();
// get the field value for
for(dataRow in fieldData) {
curData.push(dataRow[i])
}
doc.replaceItemValue(curFieldName,curData);
}
},
// retrieve the number of rows
rowCount : function(variableName:String) {
var fieldData:Array = this.getDataSource(variableName);
if ( fieldData == null) {
return 0;
}
return fieldData.length
}
}
Here is the code snippet for the repeat table :
<xp:table id=“repeatTable” style=“width:100%”>
<xp:repeat id="fieldGroup" rows="30" var="fieldGroup" indexVar="rowIndex">
<xp:this.value><![CDATA[#{javascript:var scopeVariableName = "group";
var fieldNames = “attach,attach,attach”;
tableWalker.getDataSource(scopeVariableName,FileStore,fieldNames)}]]></xp:this.value>
<xp:tr>
<xp:td>
<xp:button id="addRowBelow" icon="/actn096.gif">
<xp:eventHandler event="onclick" submit="true" refreshMode="complete" id="eventHandler2">
<xp:this.action><![CDATA[#{javascript:tableWalker.addRow("group", rowIndex+1)}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
<xp:button id="addRowAbove" icon="/actn097.gif">
<xp:eventHandler event="onclick" submit="true" refreshMode="complete" id="eventHandler1">
<xp:this.action><![CDATA[#{javascript:tableWalker.addRow("group", rowIndex)}]]></xp:this.action>
</xp:eventHandler>
</xp:button></xp:td>
<xp:td>
<xp:fileUpload id="fileUpload13" styleClass="profilesButtonCancel" value="#{FileStore.Attach}"></xp:fileUpload></xp:td>
<xp:td>
<xp:fileUpload id="fileUpload14" styleClass="profilesButtonCancel" value="#{FileStore.Attach}"></xp:fileUpload></xp:td>
<xp:td>
<xp:fileUpload id="fileUpload15" styleClass="profilesButtonCancel" value="#{FileStore.Attach}"></xp:fileUpload></xp:td>
<xp:td>
<xp:button id="deleteButton" icon="/vwicn038.gif">
<xp:eventHandler event="onclick" submit="true" refreshMode="complete" id="eventHandler3">
<xp:this.action><![CDATA[#{javascript:tableWalker.removeRow("group", rowIndex)
}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
</xp:td>
</xp:tr>
</xp:repeat>
<xp:tr>
<xp:td colspan="5" align="center">
<xp:br></xp:br>
<xp:button value="Add Row" id="addLine" styleClass="profilesButtonCancel">
<xp:eventHandler event="onclick" submit="true" refreshMode="complete" id="eventHandler4">
<xp:this.action><![CDATA[#{javascript:tableWalker.addRow("group")}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
<xp:br></xp:br> </xp:td></xp:tr></xp:table>
If anyone could help me, that would be great…its soo close!
Thanks
Jamie