Xpages get a handle on next rowData or Doc in a repeat - xpages

In my repeat control I want to be able to get a handle on data in the next row in order to modify what data is displayed in my current row (I have move up and down actions, and I don't want to display them if the move is not possible).
It seems I should be able to do this. Probably I cannot from within in the repeat itself, as when it is writing the repeat it will not know the data for the next repeat. But shouldn't there be a fairly easy way to get the next document?
Here is a reduced version of my repeat control:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:repeat
id="repeat1"
var="rowData"
indexVar="repeatIndex"
value="#{view1}"><xp:this.facets>
<xp:text
disableTheme="true"
xp:key="header"
escape="false"
>
<xp:this.value><![CDATA[<table class='lotusTable repeatRowColors' border='0' cellspacing='0' cellpadding='0' width='800px'>
<tr class ='lotusFirst lotusSort scope='col'>
<th class ='loutsFirstCell'></th>
<th class ='loutsFirstCell'>Category</th>
<th class ='lotusCenter'>Move</th>
<th>Task</th>
</tr>
</thead>]]></xp:this.value>
</xp:text>
<xp:text
disableTheme="true"
xp:key="footer"
escape="false"
>
<xp:this.value><![CDATA[</table>]]></xp:this.value>
</xp:text>
</xp:this.facets>
<xp:this.rows><![CDATA[#{javascript:var rows:Integer = viewScope.get("rows");
if (rows == null)
{return 5}
else
{return rows}}]]></xp:this.rows>
<xp:tr id="rowDataContainer">
<xp:td
style="width:55px;min-width:55px;max-width: 55px;">
<xp:text
escape="true"
id="computedField2">
<xp:this.value><![CDATA[#{javascript:rowData.getColumnValue("category");}]]>
</xp:this.value>
</xp:text>
</xp:td>
</xp:tr>
</xp:repeat>
</xp:view>

You can get view's next row within the repeat with
var nextRow = view1.getAllEntries().getNthEntry(repeatIndex + 2);
"view1" is the xp:dominoView assigned to repeat control and "repeatIndex" is the indexVar of xp:repeat.
You can get next row document's UniqueID then with
nextRow ? nextRow.getUniversalID() : ""
and the e.g. Form field with
nextRow ? nextRow.getDocument().getItemValueString("Form") : ""
Accordingly, you get the previous row with
var prevRow = view1.getAllEntries().getNthEntry(repeatIndex);

Related

#Name within repeat

I have an app I am build which will allow the users to enter the approvers for each document manually. I wanted to put this in a repeat control instead of hard coding all the fields in the xpage. All is working as I want, except the names are stored in the document in the Canonical format. I don't want to present this to the user, I want to just show the Abbreviated name. I can't seem to get it working. Below is my repeat. I want to put and #name around the value of ApproverName_#
Thanks in Advance
Walt
<xp:repeat
id="repeat4"
var="rowItem"
indexVar="indexVar">
<xp:this.value><![CDATA[#{javascript:["1", "2", "3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18"]}]]></xp:this.value>
<xp:panel>
<xp:repeat
id="repeat3"
var="fieldName">
<xp:label id="label31"><xp:this.value><![CDATA[#{javascript:"Approver - " + rowItem}]]></xp:this.value></xp:label><xp:label id="label33" value="Name"></xp:label>
<xp:this.value><![CDATA[#{javascript:["ApproverName_"+rowItem]}]]></xp:this.value>
<xp:inputText id="nameinputText"
value="#{document1[fieldName]}"
style="width:333.0px" rendered="#{javascript:!document1.isEditable()}">
</xp:inputText>
<xp:inputText id="inputText9" value="#{document1[fieldName]}" style="width:333.0px">
</xp:inputText>
<xe:namePicker id="namePicker1"
for="nameinputText">
<xe:this.dataProvider>
<xe:dominoNABNamePicker
nameList="peopleByLastName"
addressBookSel="db-name" groups="false"
people="true">
<xe:this.addressBookDb><![CDATA[#{javascript:#Subset(#DbName(), 1) + "!!names.nsf"}]]></xe:this.addressBookDb>
</xe:dominoNABNamePicker>
</xe:this.dataProvider>
</xe:namePicker></xp:repeat>
<xp:repeat id="repeat5" var="fieldName">
<xp:this.value><![CDATA[#{javascript:["Title_"+rowItem]}]]></xp:this.value>
<xp:label id="label32" value="Title"></xp:label>
<xp:inputText id="inputText10"
value="#{document1[fieldName]}">
</xp:inputText>
</xp:repeat>
<xp:repeat
id="repeat6"
var="fieldName">
<xp:this.value><![CDATA[#{javascript:["ApprovalFlag_"+rowItem]}]]></xp:this.value>
<xp:inputText id="inputText11" value="#{document1[fieldName]}">
</xp:inputText>
</xp:repeat>
<xp:repeat
id="repeat7"
var="fieldName">
<xp:this.value><![CDATA[#{javascript:["Reason"+rowItem]}]]></xp:this.value>
<xp:inputText id="inputText12" value="#{document1[fieldName]}">
</xp:inputText>
</xp:repeat>
</xp:panel>
</xp:repeat>
Use the Dojo Name Text Box instead of Input Field. It prevents typing, gives a better UX for removing entries, and does what you want out-of-the-box.

How do I display a multi-value field in a repeat?

I'm attempting to display the values of a multi-value field as an unordered list on my XPage. Unfortunately, when doing this with documents which do not already have the bound field, I'm informed 'rowData' not found for my itemValueArray. I had created a document prior to these machinations that has values in the field, so that one still opens for me.
Where have I gone wrong?
<xp:div style="display:none;">
<xp:inputText id="linkages" value="#{poDoc.Linkages}" multipleTrim="true" multipleSeparator=";">
</xp:inputText>
</xp:div>
<ul>
<xp:repeat id="linkagesDisplayRepeat" rows="30" var="rowData">
<xp:this.value><![CDATA[#{javascript:var linkages = poDoc.getItemValueArray("Linkages");
return linkages;}]]></xp:this.value>
<li>
<xp:text escape="true" id="computedField7">
<xp:this.value><![CDATA[#{javascript:rowData;}]]>
</xp:this.value>
</xp:text>
</li>
</xp:repeat>
</ul>
Error 'rowData' not found is caused by non-existing item "Linkages".
Render the ul-repeat-block only if item "Linkages" is in document
<xp:panel rendered="#{javascript: poDoc.hasItem('Linkages')}">
<ul>
<xp:repeat
...
</xp:repeat>
</ul>
</xp:panel>
As an alternative you could set a default value to document's item "Linkages" on beforePageLoad event:
<xp:this.beforePageLoad><![CDATA[#{javascript:
if (!poDoc.hasItem("Linkages")) {
poDoc.replaceItemValue("Linkages", "defaultValue");
}
}]]></xp:this.beforePageLoad>
similar to knut's second idea, you could test for a value during data binding. if there is no value, return a default array (e.g., ["data missing"]). otherwise, return the field's value(s). this way you don't have to force a value into the otherwise empty or absent field...unless that is what you want.
You can use simple EL for hide the repeat:
<xp:repeat id="linkagesDisplayRepeat" rows="30" var="rowData"
rendered="#{not empty poDoc.Linkages}">

Edit box in repeat control with default value does not update on delete

I have a Repeat control in which I have a edit box which has passed a default value.There is a Delete button to delete that row.
just for test I have used a computed field along with the edit box.Computed field is also passed the same value as edit box.Now both computed Field and Edit Box has same value,When I click on Delete button randomly,it gets delete but only computed field updates properly in repeat control wherever deleted,
In case of Edit box,it shows the last record disappears.
So the issue is, the value in edit box does not get update like computed field does.I have done the test by writing following code.
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:this.data>
<xp:dominoDocument var="document1" formName="testing">
</xp:dominoDocument>
</xp:this.data>
<xp:inputText id="inputText1" multipleSeparator=",">
<xp:this.defaultValue><![CDATA[#{javascript:var v:java.util.Vector = new java.util.Vector();
v.add('1');v.add('2');v.add('3');v.add('4');v.add('5');v.add('6');
return v;}]]></xp:this.defaultValue>
</xp:inputText>
<xp:br></xp:br>
<xp:repeat id="repeat1" rows="30" var="r" indexVar="i" first="0">
<xp:this.value><![CDATA[#{javascript:return getComponent("inputText1").getValue();}]]></xp:this.value>
<xp:br></xp:br>
<xp:div style="text-align:center">
<xp:label value="#{javascript:r}" id="label1"
style="text-align:center">
</xp:label>
<xp:button value="Delete" id="button1">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:var v:java.util.Vector = getComponent("inputText1").getValue();
if(v != null){
var sdtString = getComponent("inputText2").getValue();
if(v.contains(sdtString))
v.remove(sdtString);
};}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
<xp:inputText id="inputText2">
<xp:this.defaultValue><![CDATA[#{javascript:getComponent("label1").getValue();}]]></xp:this.defaultValue>
</xp:inputText>
</xp:div>
<xp:br></xp:br>
</xp:repeat>
</xp:view>
This is the example code for testing to get the exact issue.
Edit 1: Real Scenario
We have an application to select multiple dates for a meeting and hence using the notes free time we mark multiple dates and then users are allowed to edit/delete the same in another window..Since, it was not possible/feasible enough to save all the fields in SSJS, we rather preferred binding each field with the data-source under the repeat control by using custom properties of the custom control (hope I am making some sense). Every fields is mapped as #chintan pointed out as follows:
dataSource[compositeData.to]
As described in the first part of the question the fields are not getting updated since we set them using default values. However, based on the suggestion we tried to set the field using the script library which does work well, however, might seem stupid but it messes the date field. When we set the value using the following:
var d:java.util.Date = new java.util.Date(compositeData.selectedDate);
getComponent("from").setValue(d);
It jumbles up the month, date and year field and shows a complete different value (the same code works well when put as the default value).
Hope this makes sense.
Any kind of solution can be appreciated.
inputText2's defaultValue gets executed only once at first page load. Then the XPage memorized that repeat 1 has inputText2 defaultValue 1, repeat 2 has inputText2 defaultValue 2 and so on.
Example: as you can see in print log, inputText2's defaultValue doesn't get calculated again after deleting row 3 and later row 1.
Turn it around and set inputText2's value in label1's value code:
<xp:label
value="#{javascript:getComponent('inputText2').setValue(r); r}"
id="label1"
style="text-align:center">
</xp:label>
then it will set inputText2's value properly. You don't need inputText2's defaultValue property anymore.
As an alternative you can define inputText2's value property and bind it to a row-specific data source or viewScope variable.
As you described in your "Edit 1 Real Scenario", you want to edit and delete pairs of dates. I created the following example. Instead of dates I just use strings for simplicity. Keep the dates in a viewScope variable and edit/delete them right there:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view
xmlns:xp="http://www.ibm.com/xsp/core">
<xp:this.beforePageLoad><![CDATA[#{javascript:viewScope.dates=[
{"from":"a", "to":"b"},
{"from":"c", "to":"d"},
{"from":"x", "to":"y"}];}]]></xp:this.beforePageLoad>
<xp:br />
<xp:repeat
id="repeat1"
rows="30"
var="r"
indexVar="i"
value="#{viewScope.dates}">
<xp:br></xp:br>
<xp:div>
<xp:button
value="Delete"
id="button1">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="complete">
<xp:this.action><![CDATA[#{javascript: viewScope.dates.remove(i);
}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
<xp:inputText
id="inputText1"
value="#{r.from}">
</xp:inputText>
<xp:inputText
id="inputText2"
value="#{r.to}">
</xp:inputText>
<xp:button
value="Update"
id="button2">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="complete">
<xp:this.action><![CDATA[#{javascript: "";
}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
</xp:div>
<xp:br></xp:br>
</xp:repeat>
<xp:text
escape="false"
id="computedField1">
<xp:this.value><![CDATA[#{javascript:
var length = viewScope.dates.length;
var result = "";
for (var i = 0; i < length; i++) {
result += viewScope.dates[i].from + " - " + viewScope.dates[i].to + "<br />";
}
result
}]]></xp:this.value>
</xp:text>
</xp:view>
Create the viewScope variable as an array of objects.

Display several photos in several rows AND columns with a repeat

I would like display photos in a window on several rows and 3 columns.
image1 image2 image3
image4 image5 image6
image7 image8 image9
image10 image11 image12
Actually I find to put photo1, 2 and 3 and the others are behind photo3 but not visible...
image1 image2 image3 [image4 image5 image6 image7 ....]
This is my code :
<xp:table>
<xp:tr>
<xp:repeat id="picturesRpt" value="#{dsvListePhoto}"
indexVar="i" var="picture" rows="3" >
<xp:panel>
<xp:this.data>
<xp:dominoDocument var="pictureDoc"
action="openDocument"
documentId="#{javascript:picture.getUniversalID()}"
ignoreRequestParams="true">
</xp:dominoDocument>
</xp:this.data>
<xp:div>
<xp:td>
<xp:div styleClass="listePhotos">
<xp:div>
<xp:image id="image1">
<xp:this.url><![CDATA[#{javascript: "http://monserveur/devpt/Xpages/xphoto/PhotoWeb.nsf/" + compositeData.vueUtilisee + "/" + pictureDoc.getDocument().getUniversalID() + "/$File/TIMBRE_image.jpg?OpenElement"}]]></xp:this.url>
</xp:image>
<xp:text
value="#{pictureDoc.Titre}" />
<xp:br></xp:br>
</xp:div>
<xp:div>
<xp:text
value="#{pictureDoc.IdNiveau1} / #{pictureDoc.IdNiveau2}" />
</xp:div>
</xp:div>
</xp:panel>
</xp:repeat>
</xp:tr>
</xp:table>
An idea ?
Thanks for your help !
As you decided to create a table with three columns finish and start a table row every three pictures. Add </tr><tr> to rendered HTML whenever a new table row shall start. That is the case when indexVar i is 3, 6, 9, and so on.
Add the </tr><tr> tags with a computed field of content type HTML and use the rendered condition
(i > 0) && (i%3 == 0)
Your code would look like this:
<xp:table>
<xp:tr>
<xp:repeat
id="picturesRpt"
value="#{dsvListePhoto}"
indexVar="i"
var="picture"
rows="1000">
<xp:text escape="false">
<xp:this.rendered><![CDATA[#{javascript:
(i > 0) && (i%3 == 0) }]]></xp:this.rendered>
<xp:this.value><![CDATA[#{javascript:
"</tr><tr>" }]]></xp:this.value>
</xp:text>
<xp:td>
... picture ...
</xp:td>
</xp:repeat>
</xp:tr>
</xp:table>
Use #Modulo or some other maths tool to work out the remainder of a division by 3. That will allow you to compute whether to add Computed Text that adds a "" or a "" tag.
I did a similar demo with two columns in the Repeats on Steroids page of my IBM Connect 2013 session with Mike McGarel. The link for the slides and demo database is on my blog, at the bottom of the article, below the youtube video http://www.intec.co.uk/session-slides-and-sample-database-from-ibm-connect/
It was also delivered as a TLCC webinar, though I can't find the link. From Niklas Heidloff's blog it looks like a video was taken http://heidloff.net/home.nsf/dx/16.04.2013093214NHEAUQ.htm

Using a Repeater to show multi-value field values

I've setup a repeater and am reading the value from the multivalue field using GetItemValueArray. This returns the array and If I use a listbox it displays. I want to cross reference some other data with it though so I need to use a repeater. But I'm not sure how to have the repeater use an index that increments for each row. The code below "return rowdata[i]" doesn't recognize i.
<xp:repeat id="repeat1" var="rowdata" rows="30">
<xp:this.value>
<![CDATA[#{javascript:var myArray:Array = myDataSource.getItemValueArray("MyMultiValueFld")}]]>
</xp:this.value>
<xp:label id="lbl">
<xp:this.value><![CDATA[#{javascript:return rowdata[i];}]]></xp:this.value>
</xp:label>
</xp:repeat>
rowdata is not a reference to the myArray value as a whole, but the iterated entry in myArray. In other words... you already have what you need.
<xp:label value="#{rowdata}" />
Maybe you can simplify your code by naming using just the "rowdata" as value for your text item. You then should only change the repeat source to
myDataSource.getItemValue("myValueFld")
as this returns always an array of data. It's just depending on the datatype that this item stores, so you might have to convert in the text control.
Hi there your code has another issue, you do not have any return statement in this line:
<xp:this.value>
<![CDATA[#{javascript:var myArray:Array = myDataSource.getItemValueArray("MyMultiValueFld")}]>
</xp:this.value>
so the code does not return your myArray it only Returns the name of it as a string wich gets repeatet one time. Use this a valueBinding:
value="#{myDataSource.MyMultiValueFld}"
or add a return:
<xp:this.value>
<![CDATA[#{javascript:var myArray:Array = myDataSource.getItemValueArray("MyMultiValueFld");
return myArray;}]>
</xp:this.value>
Then you should be able to use Chris Tooheys Answer:
<xp:label value="#{rowdata}"/>
Use the indexVar property of the repeat control to setup a var containing the index.
Got it. The final piece of code is that I did have to create an indexVar="I" in my repeat1 tag but then I also had to change my label to a computed field. The final piece of code looks like:
<xp:table>
<xp:repeat id="repeat1" var="rowdata" rows="30" indexVar="i" first="0">
<xp:this.value><![CDATA[#{javascript:myDataSource.getItemValueArray("myMultivalueFld");}]]>
</xp:this.value>
<xp:tr><xp:td>
<xp:text escape="true" id="computedField1">
<xp:this.value><![CDATA[#{javascript:var repeat1:com.ibm.xsp.component.xp.XspDataIterator = getComponent("repeat1");
repeat1.getValue()[i];}]]></xp:this.value>
</xp:text>
</xp:td>
</xp:tr>
</xp:repeat>
</xp:table>
don't think I could have gotten it without the tidbit on the indexVar. Thanks
You can trim that code further. If all you need is the values, you don't need the indexvar and you don't need to reference the repeat as an XspDataIterator. Just use the rowdata as a variable.
<xp:table>
<xp:repeat id="repeat1" var="rowdata" rows="30">
<xp:this.value><![CDATA[#{javascript:myDataSource.getItemValueArray("myMultivalueFld");}]]>
</xp:this.value>
<xp:tr><xp:td>
<xp:text escape="true" id="computedField1">
<xp:this.value><![CDATA[#{javascript:rowdata;}]]></xp:this.value>
</xp:text>
</xp:td>
</xp:tr>
</xp:repeat>
Each value in the multi-value field is a "row" as referenced by rowdata. Similarly, if the data source were a view, each row would reference a document object. Don't dispose of the framework available to you by accessing the repeat as a component from inside the component itself.

Resources