Subscribe via Feed

XPages - How to pass a Document Data Source to a Custom Control

John Mackey, Aug 16, 2009 4:21:03 PM

In a previous article on my blog, I explained how you can use the undocumented currentDocument data source object to get the data source for the XPage from a Custom Control. This works great if the XPage has one data source. What if you have multiple data sources? For example, you want to include a custom control within a repeat control that is accessing multiple documents from a document collection or a view. The currentDocument variable would only point to the first data source on the XPage.

At first glance, looking at the data types that are available for parameters in a Custom Control, you cannot pass a data source object. The type list drop down is a list of standard JavaScript variable types.






But, if you click on the folder icon next to the Type field you will see an extended list of type available.



Then from your XPage you can pass the datasource using computed JavaScript, such as: "OrderDetails.getDocument();"
*updated - see note below
 

Binding fields on a Custom Control to a dynamic datasource
The second part of this can be tricky. On your Custom Control you cannot use the "Simple Data Binding" option to bind your fields since you have no datasource set for the Custom Control, the datasource is dynamic.

The perfect solution for this is to use the Advanced -> Expression Language (EL) binding. For example, to bind the field to the "Comment" field, we would use "compositeData.docdatasource.Comment". Where....
- compositeData holds all the parameters for the Custom Control
- docdatasource is the parameter that we created that receives the datasource
- Comment is the field name on the Notes form

 

Now, with that said, there is an issue with this in 8.5. DDE tries to convert the EL expression to a Simple data binding when you save and reopen the Custom Control. So it works the first time you enter it, but loses the binding when you edit the Custom Control. However, it looks like this is fixed in 8.5.1.

BUT, no sweat, I have a workaround. If you select JavaScript for the binding type, select "Compute on page load", and then enter the formula as '#{compositeData.docdatasource.Comment}' it will continue to work.

 


Here's an example of the Custom Control used with multiple Data Sources in action:

 

You can download this example here.

*update - by passing the NotesXSPDocument instead of the NotesDocument, the fields will automatically handle whether the document is in read or edit mode.

To do this, select "ModelDataSource" as the type for the property.  Then when passing the datasource from your XPage change the JavaScript from "OrderDetails.getDocument()" to just "OrderDetails".


-John


Online Product Data Entry, Data Extraction Services, Jan 13, 2012 2:20:07 AM
Online product data entry outsourcing to India can reduce your costs up to 60% and increase the productivity without any hassles. Our online product data entry includes database updation, online product entry, amazon entry, online stores data entry, catalog processing services.

Luca, Feb 25, 2010 12:06:12 PM

Hi I have a form called 'ticket' in which a have some fields with a formula (computed or default formula in editable field). This form is used in an Xpage. In the Xpage I have a custom control with a 'save' button (Type 'Submit') that sets the value of the remaining form fields and then submits the page.
Well, the result is that I get two documents: 1 with the form calculated fields, the other with the oter field set from the cutom control of the Xpage.
In the Xpage 'all properties' under the data section I have computeWithfORM=BOTH, but nothing changes if I set it to onLoad or onSave.
ThankYou!



Frantisek Kossuth, Dec 13, 2009 1:25:11 PM

@John: typeahead in SSJS editor (inside control) shows methods for com.ibm.xsp.model.DocumentDataSource class, but "real" DS is com.ibm.xsp.model.domino.wrapped.DominoDocument. is there any documentation explaining why wrapped doc doesn't work as parameter type?



John Mackey, Oct 20, 2009 6:59:11 PM

@Brian, as an additional followup, it would be better to declare doc as a NotesXspDocument. This way you have the typeahead working in the script editor. So like this:

var doc:NotesXspDocument = compositeData.docdatasource;

-John



John Mackey, Oct 20, 2009 6:39:10 PM

@Brian, I think you had it correctly. The componentData.docdatasource is a NotesXSPDocument object.

I put together this code for a ComboBox onchange event where I set another field with the value of the "Color" field.

doc=compositeData.docdatasource;
theColor=doc.getItemValueString("Color");
doc.replaceItemValue("ColorSelected",theColor);

or to get the Notes backend document, you can do:
doc=compositeData.docdatasource.getDocument();

Let me know if you still have problems.
-John



Brian Hester, Oct 20, 2009 1:47:10 PM

Hi John,

Thanks for the tip, works great.

One question, if I want to access the datasource passed to the control this way using Server Side scripts, how can I? I have a control I'm trying to convert to this that has onchange scripts that partial submit and use the datasource class functions. I tried making var obj = componentData.docdatasource but that obviously would have been too easy. Any suggestions?



Tibor, Oct 1, 2009 1:51:09 PM

Hi,

When using this expression
'#{compositeData.docdatasource.'+compositeData.fieldname+'}'
for data binding I couldn't grab the value with getComponent, it returns null.
Instead you can use this:
compositeData.docdatasource[compositeData.fieldname]
under Advanced/EL/Compute dynamically
And it doesnt't disappear after saving :)

Tibor



Irina, Aug 30, 2009 5:59:08 PM

Hi John,

This works like a charm - fantastic find!
I kept trying to use DocumentDataSource with no luck and would never think that I need ModelDataSource.
Thank you very much!

Irina



John Mackey, Aug 30, 2009 5:35:08 PM

@Andreas, yes you can pass the field name so that it is completely dynamic.

Setup a new field property named "fieldname". Set it to a String type. Then change the data binding on the field to use the new property:

'#{compositeData.docdatasource.'+compositeData.fieldname+'}'

Make sure that the Binding is set to "compute on page load". I noticed it reset mine to "compute dynamically". (I had to click off the field and then back on to see that occur).

Then on your XPage pass the field name just by typing it in as a static value with no quotes....or you can even compute that to take it to the next level.

-John



John Mackey, Aug 30, 2009 5:14:07 PM

@Irina, good catch....all my examples defaulted to edit mode so I did not notice that. Ok, I took a look at it came up with a new solution. The idea behind my new approach is that instead of passing the notesDocument, I need to pass the notesXSPDocument because that has the Action parameter which contains editDocument or readDocument as a value.

On the custom control select ModelDataSource instead of DocumentDataSource for the property type (don't even ask how I figured that out). Then on your XPage pass the datasource the same way except without the "getDocument()". So this: "OrderDetails.getDocument();" is now just this: "OrderDetails;". That way we are passing the XSPdocument.

That's it. I tested it and it works on an XPage that has datasources in both edit and read modes. Let me know if it fixes your issue and I'll amend the article.

-John



Irina, Aug 30, 2009 2:14:07 PM

Hi John, thanks for this great article!
Is it possible to specify document mode to dynamic data source?
In my example I have three documents displayed in a single Xpage. Two documents are displayed using Custom Controls and I provide data source the way you defined above. It looks just fine.
But I also need to be able to display those two documents in read or edit mode.
Even if action for those documents is "readDocument" (in the Xpage Data Source definition), on my Custom control they are always in Edit mode.
Do I need to code this into each field or is there an easier way?
Thanks!



John Mackey, Aug 30, 2009 12:22:06 PM

@Paul, the repeatControls=true is set when you click on the "create controls at page creation" option for the repeat control properties. The reason I choose that option is due to a bug. I have found that if it is unchecked I have issues adding new documents to the collection that drives to the repeat control, so the Add new Line button on this example does not work properly. This is fixed in 8.5.1.

If you would like a second pair of eyes to look at your problem, please send me an email with a sample of the control and xpage. My email is john.mackey at groupwareinc.com

-John



Andreas Olesch, Aug 30, 2009 9:37:06 AM

Found the problem. You have to select "String value" as the editor property while creating the Custom Control property.

So, i want to dynamically set the field level binding also. I would like to pass the fieldname via another property. Is this possible?



Andreas Olesch, Aug 30, 2009 9:19:05 AM

Hi!

Which Notes Designer version are you using?

I am using 8.5 FP1 and i cannot pass the NotesDocument to my control. The designer does not show the possibility to enter the code (doc.getDocument();) you show in your screenshot. I see a '+' there and different attributes to enter instead.

Where is the difference to your example?

Regards
Andreas



Paul Withers, Aug 26, 2009 1:45:05 PM

I tried using this concept because I needed to run some beforePageLoad code for each document used in the repeat. After looking at what you had, modifying my code, still finding it failing and banging my head over and over, I found the key property that was missing in my code was repeatControls="true".
Of course there's nothing in the Help files, and I can't find any reference to this property on the web. Do you have any idea why this is so crucial and what the impact is of setting it true all the time?



John Mackey, Aug 17, 2009 12:29:04 PM

@Barry, I don't think you'll encounter any lifespan memory issues with this approach.



Paul Withers, Aug 17, 2009 8:29:04 AM

Thanks, this is extremely useful. Much cleaner and presumably more efficient than the workaround I've used in the past - passing the document ID from my repeat control to a custom control, and using a datasource on the custom control.



Barry, Aug 16, 2009 6:37:03 PM

Excellent find John - this is something that had really driven me crazy.

This doesn't have any problems with the object being removed from memory, like how the viewscope works?




Post a new Comment

Name:


Comment: