Thursday, 6 January 2011

Xpages and the failing Repeat control

I am converting an application to Xpages and use the repeat control to build several levels of dynamic menu structures.
They are based on simple @DbColumn and @DbLookup. But when the result array is only one entry the repeat control fails. The code below shows the failing repeat control (xp:link simplified to keep the code clean).
[Note: single results from @DbColumn or @DbLookup are returned as string and not an array.]
 <xp:repeat id="repeat1" rows="30" var="rowData" indexVar="rowIndex"> 
<xp:this.value><![CDATA[#{javascript:
@Unique(@DbLookup("", "xpSectionList", sessionScope.regionFilter, 2))
}]]></xp:this.value>
<li>
<xp:link escape="true" id="linkSubSection" title="#{javascript:rowData}">
<xp:this.text><![CDATA[#{javascript:rowData}]]></xp:this.text>
</xp:link>
</li>
</xp:repeat>
After some searching I found a solution in the LDD by Bob Cross, "Hint for Repeating a single value".
Repeat controls are great - but they MUST return an array, even if it is just one entry.

In my example, I used a repeat control to display entries from a dblookup. However, some of my lookups only returned one entry, so the result was a string, not an array. Since the result of my dblookup was a string, the repeat control would not display the one returned result.

The solution is to convert the result to a string first by using valueOf(). This returns a string delimited by commas. Then split the result by the commas to get an array. If split fails, then the result was an array to start with.

var tmpstr = result.valueOf();
try {
var newarr = tmpstr.split(",");
return(newarr)
}
catch(err){
return(tmpstr);
}
The proposed solution works fine and does solve the problem, but I consider it a workaround for a bug. The repeat control should also work when a single value is returned.
I have filed a service request (PMR) with IBM. Please do the same if you think this should be solved!
[Note: IBM have responded to the PMR: The repeat control works as designed and expects an array as input.]
The solution workaround is implemented in the code below.
 <xp:repeat id="repeat1" rows="30" var="rowData"  indexVar="rowIndex">
<xp:this.value><![CDATA[#{javascript:
var result = @Unique(@DbLookup("", "xpSectionList", sessionScope.regionFilter, 2));
var tmpstr = result.valueOf();
try {
var newarr = tmpstr.split(",");
return(newarr)
}
catch(err){
return(tmpstr);
}
}]]></xp:this.value>
<li>
<xp:link escape="true" id="linkSection" title="#{javascript:rowData}">
<xp:this.text><![CDATA[#{javascript:rowData}]]></xp:this.text>
</xp:link>
</li>
</xp:repeat>

9 comments:

Erik Brooks said...

Do you have an SPR#?

David Leedy said...

It's early so I might not be reading this correclty... But doesn't Julians code on XPagesWiki.com solve this problem?

He has a code example that guarantees an array is returned.

http://xpageswiki.com/web/youatnotes/wiki-xpages.nsf/dx/Work_with_DbColumn_and_DbLookup

Tom Steenbergen said...

@David Leedy
Had not come accross Julians code in my search, but indeed the 2 functions at the bottom of the page do solve the problem similar to Bob Cross' solution on LDD.
In my mind it is still a workaround though for what the repeat control should handle itself.

Tom Steenbergen said...

@Erik Brooks - sorry, but no SPR# yet. PMR# is 87011 211 788.

Jeremy said...

the easiest way to do this is like this:

var r = @DbLookup(...);
return (r.constructor == Array) ? r : [ r ];

this gets the value, then compares the original array object to the object that r was constructed from. If it was an Array, then it returns r, otherwise it puts r into any array. Other functions (like julian's) check to see if the value is a string, but that may not be the case, it could be a date, or number, or whatever...

I really don't see this as a bug, and I can't see IBM changing the way this works really. The repeat works using iterators for known java types. If the object (remember a string is actually an object) doesn't have an iterator, then it really doesn't have a "set" of values ... The programmer should give the repeat what it needs to operate...

Stephan H. Wissel said...

Your code screams for optimization. Use a function that only does the @DBLookup if you don't have a value in your session already. Will improve load performance and you can take care of the array thingi

Mark Maden said...

Excellent, this thread just saved me lots of time.

Sjef Bosman said...

Just a complaint to add to the fun. A NotesViewEntryCollection works fine with a Repeat Control, but why oh
why isn't there an Iterator in the NotesViewNavigator object?

Fatih Duranoglu said...

Hi Tom

i 'm using your code and how can i remove [ ] chars.