Xpages: Cannot compute property "defaultValue" in custom control - xpages

I have a custom control which contains a comboBox. One property of the CC is default value, which I want the developer to be able to pick in the Xpage.
Static values pass into the CC fine, but if I try to default the value, it fails. I would like to default the default property to the current user, but it cannot.
This is the custom control on an the Xpage. I can return a name statically and it works, but if I compute the name it doesn't work.
<xc:cc_commonfieldselect2fromcache
datasource="#{javascript:return Ticket}"
cacheitem="employees"
fieldname="tckReqs"
fieldlabel="Requester">
<xc:this.defaultvalue>
<![CDATA[#{javascript:var usrNme:String = ("[CN]",session.getEffectiveUserName());
return usrNme;
//return "Bryan S Schmiedeler";}]]>
</xc:this.defaultvalue>
</xc:cc_commonfieldselect2fromcache>
Here is part of the custom control: I am trying to pass in the compositeData.defaultValue that was set above. If I hard code it it works, otherwise it doesn't.
<xp:comboBox
id="${javascript:compositeData.fieldName}"
value="#{compositeData.dataSource[compositeData.fieldName]}"
defaultValue="${javascript:compositeData.defaultValue}">
<xp:selectItems
value="${javascript:'#{CacheBean.'+compositeData.cacheItem+'}'}">
</xp:selectItems>
</xp:comboBox>
Here is how I have set the property in the custom control:

Your code used the defaultValue (inside your cc) before it was stored into your cc´s compositeData.
${} => computed a single time on pageload (and before all dynamically computed)
#{} => dynamically computed
You have 2 options
set your cc defaultValue to dynamically computed (#)
provide the defaultValue also on pageload ($)

In your code you compute the default value at "load time":
defaultValue="${javascript:compositeData.defaultValue}"
while you need to compute it at runtime:
defaultValue="#{javascript:compositeData.defaultValue}"
that should do the trick

Related

xp:selectItem value property throws Conversion Error

Within xp:RadioGroup there is xp:selectItem which requires itemLabel.
Data-Properties of xp:selectItem is either itemValue or value:
itemValue - is the value returned to the server
value - specifies the contest of the selection item (Can be literal string or expression)
Loading values in both value & itemValue throws an error e.g.
<xp:radioGroup id="radioGroup"
layout="lineDirection"
dojoType="dijit/form/RadioButton"
styleClass="zmdi">
<xp:selectItem itemLabel='ssh'
itemValue="serverValue"
value="SelectionContentsValue">
</xp:selectItem>
</xp:radioGroup>
The error thrown is:
Exception
Conversion Error setting value ''{0}'' for ''{1}''.
java.lang.IllegalArgumentException: Conversion Error setting value ''{0}'' for ''{1}''.
com.sun.faces.util.Util.getSelectItems(Util.java:489)
com.sun.faces.renderkit.html_basic.SelectManyCheckboxListRenderer.encodeEnd(SelectManyCheckboxListRenderer.java:130)
com.ibm.xsp.renderkit.html_basic.RadioRenderer.encodeEnd(RadioRenderer.java:48)
com.ibm.xsp.renderkit.ReadOnlyAdapterRenderer.encodeEnd(ReadOnlyAdapterRenderer.java:180)
javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:1005)
com.ibm.xsp.component.UISelectOneEx.encodeEnd(UISelectOneEx.java:331)
com.ibm.xsp.util.FacesUtil.renderComponent(FacesUtil.java:858)
Removing the [ value="SelectionContentsValue" ] attribute from the xp:selectItem renders the radioGroup without issue.
ideas? thx
The value property of the is intended to be evaluated to something that is a UISelectItem.
Here, you are assigning it to a String "SelectionContentsValue".
During rendering, the Renderer asks the radioGroup "Please give me your all your selectItems".
It does this by iterating through it's children, and checking if each child is a UISelectItem.
For each UISelectItem that it finds, it evaluates the value property.
If value evaluates to null, it will then check for itemLabel, itemValue etc. and create the selectItem using these properties.
else if value evaluates to a UISelectItem, it will use this UISelectItem
else If value evaluates to something that is not a UISelectItem, it throws an IllegalArgumentException
Your situation is throwing the IllegalArgumentException because you have assigned a String to the 'value' property instead of a UISelectItem
So you should either use the itemLabel, itemValue properties and manually set those options that way. Or instead you can use the value property to compute the selectItem to one that has been prepared in some other place, or is dynamically loaded e.g. a managedBean or scoped variable.
I hope this helps let me know if any questions
Thank you Cameron, you comment sparked an idea to go track down, which I found here:
IBM Domino Designer> IBM Domino Designer 9.0.0> IBM Domino Designer User Guide> Designing XPages applications> Adding controls> Control reference selectItems - Selection Items
» Checkboxes can each checked "true", no mutual exclusivity with checkboxes.
Checkboxes are kinda like Hippies, "It's all cool man".
» Radio buttons are much more finicky:
» Radios require itemLabel with itemValue aliase optional, for each selection option:
» The 'value' data-property is for checkboxes. Value contains pipe-delimited selection values, in an array/vector of checkbox choices.
<xp:checkBoxGroup id="checkBoxGroup1" value="#{document1.fruit}">
<xp:selectItems>
<xp:this.value>
<![CDATA[${javascript:["Apples|apples|Garden of Eden|false","Oranges|oranges"]}]]>
</xp:this.value>
</xp:selectItems>
</xp:checkBoxGroup>

Xpages and computed field in a composite control

I've been able to successfully create dynamic field names and save the values for input fields using the technique described here: http://lpar.ath0.com/2014/04/07/repeated-xpages-input-controls-with-number-indexed-field-names/
I also have a computed field which has a number indexed name but whose value is computed based on a keyword choice. I can assign the dynamic field name for it like so:
<xp:text escape="true" id="computedField1">
<xp:this.value><![CDATA[#{compositeData.dataSource[compositeData.fieldName2]}]]></xp:this.value>
</xp:text>
The property definition for this field looks like this:
and the call to the composite control looks like this:
<xc:cc_dynamicAssetItems
row="#{(rownum lt 10)? '0':''}#{rownum}"
dataSource="#{document1}"
fieldName1="replace#{(rownum lt 10)? '0':''}#{rownum}"
fieldName2="budget#{(rownum lt 10)? '0':''}#{rownum}" >
</xc:cc_dynamicAssetItems>
I am at a loss however on how to pass the value to this computed field. The SSJS for it would have been:
var projectNumber = getComponent("ProjectNumber").getValue();
if(projectNumber == ""){
return "Nothing found";
}else{
return projectNumber + "A";
}
I'd appreciate some direction.
Thanks,
Dan
Instead of setting the value of your component, you should set the value of the underlying datasource. This means that your logic should not run in your computed field, than in your onChange event of the ProjectNumber component.
Then you have to update e.g. document1.budget01 only, which is a lot faster.
As an alternative, you can still pass a method binding* to your custom control, as described here: Pass javascript code to Custom Control
(*: in your case a value binding)
did you try javascript instead expression language
ex:
fieldName1="#{javascript: 'replace'+(rownum > 10? '0':'')+rownum}"

How do I compute the Selected property of a BasicLeafNode for a Dynamic Content Control - Updated 03/26/2014

I have created an XPage with the following: Started by creating a custom layout control using the Application Layout. I aded the layout control to the xpage and then dropped in a Dynamic Content Control. I configured the control as follows:
<xe:dynamicContent id="dynamicContent1" defaultFacet="GovernanceReviews"
useHash="true">
<xp:this.facets>
<xc:ccViewDocumentTemplates xp:key="DocumentTemplates"></xc:ccViewDocumentTemplates>
<xc:ccViewGovProcurementReviews xp:key="GovProcurementReviews"></xc:ccViewGovProcurementReviews>
<xc:ccViewGovRevReporting xp:key="GovRevReporting"></xc:ccViewGovRevReporting>
<xc:ccViewGovRevWOCompleted xp:key="GovRevWOCompleted"></xc:ccViewGovRevWOCompleted>
<xc:ccViewGovernanceReviews xp:key="GovernanceReviews"></xc:ccViewGovernanceReviews>
<xc:ccViewProfilesByType xp:key="ProfilesByType"></xc:ccViewProfilesByType>
<xc:ccViewProfilesWithTargetCompl xp:key="ProfilesWithTargetCompl"></xc:ccViewProfilesWithTargetCompl>
<xc:ccViewLastUpdated xp:key="LastUpdated"></xc:ccViewLastUpdated>
<xc:ccViewUserGuide xp:key="UserGuide"></xc:ccViewUserGuide>
<xc:ccViewTracking xp:key="Tracking"></xc:ccViewTracking>
</xp:this.facets>
</xe:dynamicContent>
Then I dropped in a navigator control in the left column and created BasicLeafNodes to correspond to the dynamic content control I used the href property and used the #content="" to display the correct content.
This works just fine, but I am having problems figuring out how to make the selections in the navigator highlight when they are selected. I know I need to compute the Selectd property,but I can't figure out how to get the xp:key value so I can compare it to the SubmitValue. I know this is probably something simple, but I can't figure it out.
Can someone please enlighten me.
Thanks,
MJ
ADDED 03/26/2014 - I have a feeling that it has something to do with Using the href property of the Dynamic Content Control to perform the content switching. I know that makes the BasicLeafNodes Links. So, not sure how the Navigator records which link is being executed and how to capture that.
MJ
Add a value is the submitValue property
And in the onItemClick Event
Assign the submitted value to a viewScope variable
viewScope.Selected=context.getSubmittedValue()
And finally check if the viewScope variable equals your item submit value in the selected property. This needs to be calculated
if(viewScope.Selected="byCategory"){
return true
}else{
return false
}
The following is working for me:
if(viewScope.Selected == "byCategory"){
return true
} else{
return false
}
An equality test must be made with two equal symbols (or three). One equal symbol evidently always returns true.
I did it by jQuery. Just put the following code to the custom control, which contains navigator.
$( function() {
if (window.location.hash.length > 0) {
select()
}
});
$(window).on('hashchange', function() {
select();
});
function select() {
$(".lotusColLeft .lotusMenu .lotusBottomCorner li").removeClass(
"lotusSelected")
$(".lotusColLeft .lotusMenu .lotusBottomCorner li a")
.filter(
function() {
return window.location.hash.indexOf($(this).attr(
'href')) > -1
}).parent().addClass("lotusSelected")
}

How to set the pageTitle property for all pages that use a custom control?

Would like to automatically compute the pageTitle property on the XPage to simply show the XPage name. I want to code this once on the Application Layout custom control so every page where I add the control gets the benefit of the property calculation. So far, I have some SSJS on my CC to calculate the page title and assign to a viewScope variable:
var path:String = context.getUrl().getPath();
var xpageName:String = #RightBack(path,"/");
viewScope.xpageName = xpageName;
return xpageName
On any XPage where I add the CC I can simply return the value of the viewScope variable to the pageTitle property like this:
viewScope.xpageName
However, was wondering how to automatically set it from the CC without the need for the line above. Can this be done?
You can also set the pageTitle in a theme. This page from Julian Buss's site shows code for defaulting the value http://xpageswiki.com/web/youatnotes/wiki-xpages.nsf/dx/Work_with_themes. Because override is set to false, you can override it on any custom control or XPage you choose. This is one of the theme properties I set in all applications.
You can do this as Declan says in the All Properties property on the Custom Control. I prefer to put this in the Database Theme using a control block like this:
<control override="true">
<name>ViewRoot</name>
<property>
<name>pageTitle</name>
<value>#{javascript:
var path:String = context.getUrl().getPath();
var xpageName:String = #RightBack(path,"/");
viewScope.xpageName = xpageName;
return xpageName
}</value>
</property>
</control>
This then forces your code to use the ssjs code for all XPages delivered in the database. I actually prefer that the return value be:
return database.getTitle() + " : " + xpageName;
Enjoy
/Newbs
In the 'All Properties' panel of the custom control there is a 'pageTitle' property that you can use. Once set it will be used for the title of the page as long as there is no pageTitle property set on the main XPage and as long as no other custom control on the same page have the property set.

Xpages more fields (unlimited) at the click of a button

I would like to start with x no. of fields (in my app I have a pair of textual data field and numeric data field) on a xpage application (say 10 pairs) and then when the user clicks on "more field", I want more pairs to appear dynamically without a full refresh on the page, but would like unlimited no. of fields (as long as the page doesn't crash) and then I would like to submit the form and the data for all those fields. What's the best way to implement this?
Usually, fields are bound to a document data source using dot notation:
<inputText value="#{contact.firstName}" />
However, array notation is also supported:
<inputText value="#{contact['firstName']}" />
Because the field name in this latter syntax is being treated as a string, not as an implicit property of the bean, it can be dynamically computed. What I've found to be the easiest way to define these dynamic fields is to create a custom control for each of the basic types of fields, and define each as accepting the data source and the field name. So the field itself then ends up with a syntax similar to the following:
<inputText value="#{compositeData.dataSource[compositeData.fieldName]}" />
By using that syntax, a calculation of any complexity can be used to determine what field name to pass to the custom control. In the scenario you're attempting to accomplish, specifying an indexVar on the repeat control that surrounds the field pair would allow you to designate a field suffix for each... perhaps something like the following:
<xp:repeat indexVar="fieldSuffix" value="#{viewScope.rowCount}">
<xp:div>
<xc:dynamicInputText dataSource="#{contact}" fieldName="fullName_#{fieldSuffix}" />
<xc:dynamicInputNumber dataSource="#{contact}" fieldName="phoneNumber_#{fieldSuffix}" />
</xp:div>
</xp:repeat>
With this approach, you would end up with fields named "fullName_0", "fullName_1", etc., up to the limit specified in the viewScope. Typically, the only complication is ensuring that when an existing document is opened, the viewScope variable is set back to the correct limit. Another approach to that, of course, is actually saving the limit as another item on the document and binding the repeat value to it instead.
You also can have a look at the exercise 23 "Tablewalker". It doesn't do multiple fields but does Multi-value fields which might be better in terms of processing and storage (you can do an #Elements to find out how many are there in a document). The exercise is here:
http://www-10.lotus.com/ldd/ddwiki.nsf/dx/Tutorial-Introduction-to-XPages-Exercise-23
While the button only adds one row at a time, it is easy to adjust.
What you could do is have a Bean with 2 String values Label and Data and a managed bean that has a ArrayList of that object so inside of your repeat control you bind the repeat to the ArrayList and then bind your xp:inputText to rowData.Data and your xp:label to rowData.Label then when you want to add another 5 rows you just add However many more objects into the ArrayList then refresh your page, your data will still live in your arraylist and you will have 5 new Empty objects where you can add data.
public class Data {
private String label;
private String data;
public Data() {
}
//getters and setters
}
public class ManagedBean {
private ArrayList<Data> datalist; // add a managed property for this one so It will create a new one when needed.
// getters and setters
public addFiveMoreObjects() {
Data newItem;
for (int i=0; i<5; i++) {
newItem = new Data();
datalist.add(newItem);
}
}
}
<xp:repeat value="#{managedBean.datalist}" var="rowData">
<xp:text value="#{rowData.label}" />
<xp:inputText value="#{rowData.data} />
</xp:repeat>
<xp:button value="Add 5 More"> // call #{managedBean.addFiveMoreObjects}

Resources