Thursday, March 3, 2011

Repeat Controls and Multivalue fields

There was a small discussion over at Julian's blog regarding multivalue fields and repeat controls. I knew that it was possible to bind Javascript objects and arrays to input fields inside a repeat control using EL syntax.

I got a little curious, and decided to test if the same was possible for a multivalue field. It turns out it's just as easy.

Instead of my regular demoapps, I'll post source code for the test XPage I made. It's bound to a form with one field, named field (don't name any field field in real applications!!).

To test it, create a form named Test with a field named field. Create an XPage/paste the source code from the file below. Click the add-button to add an item in the multivalue field. Save the document to test if it works. Not sure how many versions back this works with.

Demo XPage source code

24 comments:

Karthikeyan A said...

Hi Tommy,

Please help me with the following. I understand that clicking on the page numbers in a pager present in a XPage view control triggers a full page refresh.

Is it possible to make it partial or confine the refresh to a panel that contains the view control.If yes please let me know that.

I have been breaking my head on this for over a week now :(

Please help me with this one

Thanks in advance :)

Tommy Valand said...

Strange.. By default, the pager should partially refresh the view panel.

Are talking about a custom pager of some sort?

Anonymous said...

Thanks for the post Tommy...extremely useful.

I'm trying to figure out how to get a handle on the values of the individual fields that get created on the XPage. Would getItemValueArray be a way to do this?

Tommy Valand said...

Are you talking about getting the value array from the field bound to the repeat control in a similar situation as the demo? If so, you can use getValue.

Anonymous said...

Sorry, I see it now. Thanks!

Ryan said...

I noticed that if I only add 1 item and try to save I get an error:

"Error getting property '0' from bean of type java.lang.String"

javax.faces.el.PropertyNotFoundException: Error getting property '0' from bean of type java.lang.String

Anyone else come across this?

Ryan said...

Edit:

The "Error getting property '0' from bean of type java.lang.String" is because I was entering a number instead of a text string I believe.

However, when adding only only one item and saving, the repeat control will not display the data. It seems the items need to be greater than 1 for the repeat to show.

Tommy Valand said...

To work around this, you need to have a single editable field outside the repeat control that's rendered when the field has a single value.

I've updated the source code for the proof of concept XPage. Take a look at it, and see if you can make your app work.

Ryan said...

Yes, that works. Thanks for the help!

I'm trying to extend this example to be able to include multiple edit boxes in the repeat. So onclick of "Add item" repeats field1, and field2 for example and builds an array for each. Have you come across any examples of people using a repeat like this?

I've tried adding another edit box control to the repeat and adding the necessary fields but to no success. I'll keep hacking away.

Tommy Valand said...

Let me know how it goes. If you can't make it work, I can try to make a more complex proof of concept.

Ryan said...

Here is what I came up with for multiple controls: http://dl.dropbox.com/u/40159/MultiValueRepeatMultiControls.txt

Only thing with this is that it creates the control directly underneath the previous one like so: http://dl.dropbox.com/u/40159/MultiControl1.png

What would be better is if it were to just repeat the entire repeat block and place it underneath the previous one like: http://dl.dropbox.com/u/40159/MultiControl2.png

You have any ideas? If not, no worries. You've been a great help. Thanks!

(not sure if my link tags will work in this post. may have to copy/paste)

Tommy Valand said...

I created another proof of concept. The form it uses has two fields, field1 and field2.

Source code

This works as you describe.

Ryan said...

Works fantastic. I could see this being useful for many applications. Thanks a million for the assistance.

Tommy Valand said...

No problem :)

Ryan said...

Tommy,

I noticed if you add an item (so there are 2 sets of fields with data in each control) and Save. Then Edit without adding another item and add any data to the existing fields and Save, none of the data/fields get updated. Could this be a problem with the viewScope not getting updated?

I was also trying to add the ability to delete a repeat row. I came across an article (http://goo.gl/IVgl8) that used the method: RepeatCollectionName.removePermanently(true) in an onClick event. When I try this I get: Error calling method 'removePermanetly(boolean)' on an object of type 'String [JavaScript Object]'. I assume this won't work because those fields are in an array?

Tommy Valand said...

I'll try to take a look at the "edit not working" problem when I get the time.

Regarding deleting. Try something like this (loop on the field names array). If typeof document.getValue( fieldName ) === 'string' -> document.setValue( fieldName, ""), else document.getValue( fieldName ).remove( index ).

Sometimes you can use the java Vector API on arrays in SSJS. I'll take a look at deleting rows as well if the above doesn't work.

Ryan said...

Tommy,

This worked with one minor modification. Instead of .remove( index ) I needed to use .remove( itemIndex )

Thanks!

Tommy Valand said...

I tried to find a workaround for edit not working, but I couldn't find anything that worked.

The only way I can think of to work around this is to bind the fields to an array of objects, and setting/transferring the values in the querySaveDocument event. Create the object-array based on the field values in the afterPageLoad.

Bind the object to a viewScope variable.

If you can think of another, less hacky way to achieve what you want to do, that's probably the best solution. Hacks can sometimes be hard to maintain, as you might forget how they work.

Ryan said...

I came up with this on a Save button: http://dl.dropbox.com/u/40159/MultiNewSave.txt

This half works. It will successfully save and update the first field, Field 1, but not Field 2. And if I change:



to swap the fields in the put (e.g. [ 'Field2', 'Field1' ]... It will successfully update Field 2, but not Field 1. Any ideas? Maybe this just comes down to the viewscope issue, or maybe I'm just not getting the array of values in my save event?

Ryan said...

if I change: code got deleted. Supposed to show switching the code in the this.beforePageLoad:

viewScope.put( 'multivalueRepeatFields', [ 'Field2', 'Field1' ] )

Ryan said...

Somehow my previous post was deleted.

I created a new save button that half works for multiple save issue: http://dl.dropbox.com/u/40159/MultiNewSave.txt

This save button will update and save Field 1, but not Field 2. If I switch the code in this.beforePageLoad:

viewScope.put( 'multivalueRepeatFields', [ 'Field2', 'Field1' ] )

It will update and save Field 2, but not Field 1.

Any ideas? Maybe I'm just not getting all the values in my save event, or maybe it has to do with the viewscope?

Anonymous said...

Hi Tommy ,

I am trying to get repeat control working for the multi-value filed. It use new line as value separator and to display on the form. When i put it in repeat control all text appears on one line. Any suggestions what could be causing this?

thanks in advance for your help and time

Milind Apte
milind_apte@hotmail.com

Tommy Valand said...

It's hard to say without some source code.

If you just want to split values based on line breaks, a custom converter is probably the way to go (unless IBM has fixed the bug).

Take a look at the code in this post:
http://dontpanic82.blogspot.com/2010/06/xpages-code-snippet-for-multi-value.html

Henning Schmidt said...

Hi Tommy,

just found this blog post recently and it helped me solve one of my problems. Thanks!

I found a solution to save the field after editing it that I want to share. I added this line of code to the querySaveDocument event of the datasource:

document.replaceItemValue("field",document.getItemValue("field"));

It's strange but it works. ;-)