Wednesday, January 6, 2010

XPages: Binding customizable scoped variables to fields in custom controls

Edit 13.09.2012: As commenter, Bas van Gestel pointed out. This method of doing things is outdated. I can't remember the reason why I had issues, but this is a better way of doing it (where fieldName is a custom property):
#{viewScope[compositeData.fieldName]}
I stumbled onto a weakness(?) in the XPages editor today. I have a complex field that I use twice in an XPage. The only difference between the two instances of the field is the rendered-property, and the data binding. To avoid copy/pasting(/screwing up in the long run), I decided to make a custom control of the field.

The rendered-property is set on the custom control, instead of the field. The data binding is a scoped variable. I couldn't find a way to compute the name of the scoped variable in the data binding section. To work around this (flaw?), I set the data binding afterPageLoad.

Here's the code:
// Set value-binding when page loads
var application = facesContext.getApplication();
var scopedField = compositeData.scopedField;
var valueBinding = application.createValueBinding( '#{viewScope.' + scopedField + '}')
getComponent( 'text-filter' ).setValueBinding( 'value', valueBinding );
text-filter is the id of the field.
scopedField is the custom property that defines the name of the scoped variable

If you have a custom control that's used many times inside a page, you may need private scoped variables.

Share and enjoy!

6 comments:

John Smart said...

Wow!

This is what I was searching for, but now I have a meta-question that would help me more in the future: How in the heck did you figure that out?!? :-)

Did you use your API Inspector to see that a component has a setValueBinding() and then play around from there or was there something more to it?

Tommy Valand said...

I can't remember exactly, but I think I used the API Inspector (v1) to find a method that sounded something like the thing I wanted to do.

Then I googled for JSF-related information about the method.

Sjef Bosman said...

It works perfectly on scoped variables, but what I want maybe is too difficult. I want to make it work on the following EL expression (which actually works, but I really need the SSJS equivalent):

compositeData.dataSource[compositeData.fieldName]

Yes, I pass currentDocument in dataSource and the name of the field in fieldName.

Here's what I tried:

var application = facesContext.getApplication();
var scopeKey = compositeData.fieldName;
var valueBinding= application.createValueBinding('#{compositeData.dataSource[\'' + scopeKey + '\']}');
print("frmla: " + valueBinding.getExpressionString());
print("value: " + valueBinding.getValue(facesContext));
getComponent('uniqueVpTiers').setValueBinding('value', valueBinding);

and no data in the field...

Maybe you have another brilliant tip for me?

Best regards,
Sjef (at bosman dot fr)

Tommy Valand said...

Not sure if I understand what you want, but..

You should be able to do something like this on the valueBinding:
var valueBinding= application.createValueBinding('#{currentDocument.' + scopeKey + '}');

Let me know if it works for future reference.. :)

Bas van Gestel said...

Why not just use:
#{viewScope[compositeDate.fieldName]}

Seems to work fine here, even in a repeat I can use:
#{viewScope[compositeData.fieldDefinition[indexVar].fieldName]}

Tommy Valand said...

I can't remember the details for this issue I had. Could be that I wasn't taking advantage of the source editor at the time, or that the validation of the source in 8.5.0 or 8.5.1 (not sure which version we had at the time of writing) resulted in errors.

Anyways, today I would use something like what you suggest. :)