bind xPages combobox not working properly - xpages

I have a simple combobox that is bind to some combobox field on Notes form.
The problem is that if I open a document with value that is not in the list it sets the xPage combobox to first available instead of blank. I.e. notes document has value e.g. "Done" in the field. But the combo shows "New" instead of blank when I open it in web.
<xp:comboBox value="#{document1.Status}" id="Status">
<xp:selectItems id="selectItems1">
<xp:this.value><![CDATA[#{javascript:var arr = new Array(" ");
arr.push("New");
arr.push("Opne");
arr.push("Cancel");
arr.push("In Progress");
arr.push("Done");
return arr;
}]]>
</xp:this.value>
</xp:selectItems>
</xp:comboBox>

Related

how to store and save correctly field values from repeat controls

I created a simple repeat control, having inside it 2 simple inputTexts. Outside this repeat control, there is an editable inputText3 ( binded to dataSource ). I'm using it to make some calculations inside the repeat control. The calculations are displayed correctly. I have 2 buttons, for newLine and delete/hideLine. For the moment, those 2 fields inside the repeat control are not binded to the datasource.
i have a button which save the doc., and list it inside a viewPanel, having the first column inputText3, clickable. I noticed that if I open the document ( in edit or read mode ) this editable field value is correctly displayed, but the fields inside the repeat control are all null, even if I add some values before using the save method.
I try also to create 2 fields inside the form ( / datasource ) and binding this inputTexts to them, but again the repeat control fields are empty.
<xp:repeat id="repeat1" var="varRepeat" indexVar="index">
<xp:this.value><![CDATA[#{javascript:parseInt(sessionScope.dField)
}]]></xp:this.value>
...
<xp:inputText id="inputText1"></xp:inputText>
<xp:inputText id="inputText2"></xp:inputText>
The editable field outside the repeat:
<xp:inputText id="number" value="#{docrepeat.valtotala}"
defaultValue="100">
</xp:inputText>
.
<xp:this.data>
<xp:dominoDocument var="docrepeat" formName="docrepeat"></xp:dominoDocument>
</xp:this.data>
I'm definitely missing something here, hope to reach to a functional solution.
Should I bind the 2 inputText using the var property of the repeat?
Or how can I achieve this?
repeat control code:
<xp:repeat id="repeat1" var="test" indexVar="index" rows="8">
<xp:this.value><![CDATA[#{javascript:parseInt(sessionScope.DField)
}]]></xp:this.value>
<xp:panel>
<xp:table >
<xp:tr>
<xp:td>
<xp:inputText id="inputText1">
<xp:eventHandler event="onchange"
submit="false">
<xp:this.script><![CDATA[try
{
var idx="view:_id1:inputText3";
var index=document.getElementById(idx).value;
var number="view:_id1:number";
var val=document.getElementById(number).value;
var sum = val;
for(var i=0;i<index;i++) {
var input1="view:_id1:repeat1:"+i+":inputText1"
var nr1=document.getElementById(input1).value;
sum-=nr1;
document.getElementById("view:_id1:repeat1:"+i+":inputText2").value = sum;
}
// calculating some %
document.getElementById("view:_id1:test").value = parseInt((sum*100)/val);
}
catch(e)
{
alert("not working");
}]]></xp:this.script>
</xp:eventHandler>
</xp:inputText>
<xp:inputText id="inputText2"></xp:inputText>
</xp:td>
</xp:tr>
</xp:table>
</xp:panel>
</xp:repeat>
My little scenario was described here: Xpages how to obtain/create this calculations module
I think your datasource inside the repeat is a view. You need to add a datasource of object data inside the repeat. The way to do this is to create a panel inside the repeat and give that panel a datasource as follows.
<xp:repeat id="repeat1" rows="30" value="#{view1}"
var="rowData">
<xp:panel>
<xp:dominoDocument var="objectData1"
formName="Item"
documentId="#{javascript:return rowData.getNoteID();}"
ignoreRequestParams="true" action="openDocument">
</xp:dominoDocument>
!!!put your stuff in the panel!!!
</xp:panel>
</xp:repeat>
So what is going on is the repeat grabs the datasource which is a view. So it will iterate through all of the entries in the view. But the view datasource does not know what to do with documents. So you create the objectData which will grab the noteID of that specific document and make that available to the repeat as a document(referenced by noteID). Making it available as a document will allow it to save values. You probably won't be able to use the value picker but just type in the field names and it will work.
Not sure I am completely understanding your problem. But you also need to save the datasource. So you could either put a save button in the panel to save that specific document or make the save button save all data sources. I prefer being able to save each document separately as it allows for applications that multiple can edit at the same time.

xpages hiding/showing fields based on a combobox value

I'm using a <xe:djTabContainer> with 10 <xe:djTabPane> containing numerous fields components.
There is a principal combobox whose value will determine which fields to be showed or not and in this way the document structure will be achieved.
If I will use this approach, then for all my >50 fields which I want to show/hide, I will use only the onChange event of the combobox?
Considering the fact that there are >50 fields which enter in this category < showing&hiding them >, should I use another approach / method? Thanks for your time.
<xp:comboBox value="#{Contr.txt_tipcontractcv}" id="comboBox4"> <xp:selectItems id="selectItems1">
<xp:this.value><![CDATA[#{javascript:return ""}]]></xp:this.value>
</xp:selectItems>
<xp:selectItems id="selectItems2">
<xp:this.value><![CDATA[#{javascript:#DbColumn(#DbName(),"SetupvwTipuriContracteC",1);}]]> </xp:this.value>
</xp:selectItems>
<xp:eventHandler event="onchange" submit="false"> <xp:this.script><![CDATA[XSP.partialRefreshPost("#{id:FisaP}", {
});
]]></xp:this.script> </xp:eventHandler> </xp:comboBox>
and the panel:
<xp:panel id="FisaP">
<xp:label id="label4"
style="color:rgb(128,0,0);font-family:verdana;font-size:9pt;font-weight:bold">
<xp:this.value><![CDATA[#{javascript:"Fisa contract "+ Contr.getItemValueString("txt_tipcontractcv")}]]></xp:this.value>
<xp:this.rendered><![CDATA[#{javascript:
Contr.getItemValueString("txt_tipcontractcv") != ""
}]]></xp:this.rendered>
</xp:label>
</xp:panel>
I would turn it around. Let the labels and fields ask the combobox if they should be rendered or not. Let the combobox's onchange event initiate a partial refresh of a panel which includes all fields you want to show/hide.
If your >50 fields are all on one place you can frame them with a panel and set the rendered property there.
If your combobox is bound to a viewScope variable the rendered property of fields/labels would be
rendered="#{javascript:viewScope.tipcontractcv1 == 'Vanzare-Cumparare'}"
or if it is bound to a document field then
rendered="#{javascript:document1.getItemValueString('txt_tipcontractcv1') === 'Vanzare-Cumparare'}"
Update:
Based on your code in your answer https://stackoverflow.com/a/25636661/2065611 take the following steps so get it to work with the Dojo Tab Container:
1.
Put the labels and fields in panels which do have an id but don't have a rendered attribute
<xp:panel id="panel1">
<xp:label value="Persoane spre informare" ... id="label2">
<xp:this.rendered><![CDATA[#{javascript:
Contr.getItemValueString("txt_tipcontractcv1") == "Vanzare-Cumparare"
}]]></xp:this.rendered>
</xp:label>
... other label and fields ...
</xp:panel>
You can create other panels "panel2", "panel3", ... too. They can be placed in different djTabPanes.
2.
Change the onchange event of your combobox and execute client side code to refresh the panels
<xp:eventHandler
event="onchange"
submit="false">
<xp:this.script><![CDATA[
XSP.partialRefreshPost("#{id:panel1}", {
onComplete: function() {
XSP.partialRefreshPost("#{id:panel2}");
}
});
]]></xp:this.script>
</xp:eventHandler>
3.
You can optimize your code if you put labels and fields with the same rendered attribute together into an additional panel
<xp:panel id="panel1">
<xp:panel id="panelRendered1"
<xp:this.rendered><![CDATA[#{javascript:
Contr.getItemValueString("txt_tipcontractcv1") == "Vanzare-Cumparare"
}]]></xp:this.rendered>
<xp:label value="Persoane spre informare" ... id="label2" />
... other label and fields ...
</xp:panel>
</xp:panel>
First... never do this:
if (comboVal == "Vanzare-Cumparare")
Even though it's called "SSJS".. it's really not "JavaScript"... you're pretty much working with Java. In Java everything is an object. Even a literal string. So by entering "Vanzare-Cumparare" you're pretty much creating a new Object. You can see this in the typeahead of the SSJS editor. Try typing "anything". <-- Note you NEED to type in that period.
The way to do that if statement is:
if ("Vanzare-Cumparare".equalsIgnoreCase(comboVal)
you could reverse it I think:
if (comboVal.equalsIgnoreCase("Vanzare-Cumparare")
Should give you the same result. I think sometimes you can get away with using the == but mostly likely it's going to bite you at some point no matter what. So I recommend never doing that.
I'm not sure I'm following your approach here. I guess it makes some sense but if it were me I'd do it differently. I know that personally I've NEVER tried to grab the component in SSJS like this: var combo:javax.faces.component.UIComponent - I've never seen the need.
I'd prefer to use a scoped variable and then in the label's rendering property grab that scoped variable and use that to determine your rendering value - true or false.
I see a real problem. If the component is not currently rendered, you cannot get a handle to it with getComponent. If you simply change the display value, then the component is always there, even if it's not displayed.
Something like this:
var combo:javax.faces.component.UIComponent = getComponent("txt_tipcontractcv1");
var comboVal = combo.getValue();
if (comboVal.equalsIgnoreCase("Vanzare-Cumparare")) {
document.getElementById("#{id:label2}").style.display = "block"; // or "inline"
} else {
document.getElementById("#{id:label2}").style.display = "none";
}
You need to ensure that the label2 element is always rendered, so that the style indicates whether it is visible.

Strange behaviour with Typeahead - search string returned along with results

Apologies for the length. I'm having a play with fancy typeahead, and I ran across an issue that I can't explain. I've found a fix, but the fix doesn't make sense, so I'm wondering if anybody can look at my example and explain what is happening, and if it is a bug of some sort? I have stripped out most of the functionality to provide a bare bones example to demonstrate.
The scenario: I've got a text box, with fancy typeahead. Beside that, I have a button to append the newly found value into a second field. I use the second field to drive a repeat control showing all values chosen to date. Code below
The result: To test this, type 'ab' in the field, and select any choice, then click on the 'add' link. The repeat shows (, abcd) as you would expect. Test 2, repeat that process twice. The repeat will show (, abcd, ab, abcd). For some reason, the 'ab' is being added into the second field. The third test - type ab, choose a choice, add. Type 'bc', choose a choice, add. Type cd, but instead of choosing a choice, click the save button. The repeat then shows (, abcd, bc, abce, cd). I can't explain that at all.
The workaround: To get this to work properly, I can change the link's refresh type from partial to full. I have no idea why this makes it work, but it does. I don't know if I really want to do that, but I will if I have to. This is on a fairly complex form and I'd rather not do a full refresh unless I have to.
The code: Here's a cut-down xpage that demonstrates the problem:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core" xmlns:xc="http://www.ibm.com/xsp/custom">
<xp:this.data>
<xp:dominoDocument var="document1" formName="Test">
</xp:dominoDocument>
</xp:this.data>
<xp:panel id="mainPanel">
<xp:button value="Save" id="button1">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action>
<xp:saveDocument var="document1"></xp:saveDocument>
</xp:this.action>
</xp:eventHandler>
</xp:button>
<xp:inputText id="objName1" value="#{document1.objName}">
<xp:typeAhead mode="full" minChars="2" valueMarkup="true"
var="searchValue" id="typeAhead1">
<xp:this.valueList><![CDATA[#{javascript: //In here I usually do some searching to find a result set, but for the purposes of a test, let's just do an array
var mapResults:java.util.TreeMap = new java.util.TreeMap();
mapResults.put("abcd", "abcd");
mapResults.put("abce", "abce");
mapResults.put("abcf", "abcf");
mapResults.put("abcg", "abcg");
mapResults.put("abch", "abch");
mapResults.put("abci", "abci");
//Now format the results
var returnList = "<ul>";
//All results are in the TreeMap and are sorted. Now add them to the output
var iter:Iterator = mapResults.entrySet().iterator();
while (iter.hasNext()) {
var nextEntry = iter.next();
returnList += "<li>" + nextEntry.getValue() +"</li>";
}
//Lastly, close off the UL tag and return
returnList += "</ul>";
return returnList;}]]></xp:this.valueList>
</xp:typeAhead>
</xp:inputText>
<xp:link escape="true" text="Add Another Object" id="link1">
<xp:eventHandler event="onclick" submit="true"
refreshMode="partial" id="eventHandler1" refreshId="mainPanel">
<xp:this.action><![CDATA[#{javascript:var currentVals = document1.getItemValueArray("addObjName");
//check for null value in first item in array - happens
//if we clear the array from the 'cross' buttons in the repeat
if (currentVals[0] == null) {
currentVals = new Array("");
}
//get the value from the object name field and whack it on the end
var newValue = getComponent("objName1").getValue();
//Put the value into a test field so we can prove that the value wasn't added below
document1.replaceItemValue("test1", newValue);
//Now add it to the field - where is the search text coming from?
currentVals.push(newValue);
document1.replaceItemValue("addObjName", currentVals);
getComponent("objName1").setValue("");
}]]></xp:this.action>
</xp:eventHandler>
</xp:link>
<xp:repeat id="repeat1" rows="30" var="rowData" indexVar="index"
repeatControls="false">
<xp:this.value><![CDATA[#{javascript:document1.getItemValueArray("addObjName")}]]></xp:this.value>
<xp:panel tagName="div">
<xp:link escape="true" id="link2" title="Remove from list">
<xp:image url="/vwicn081.gif" id="image1">
<xp:eventHandler event="onclick" submit="true"
refreshMode="partial" refreshId="mainPanel" id="eventHandler2">
<xp:this.action><![CDATA[#{javascript://remove an entry (string) from a list of strings (thanks to Mark Leusink)
Array.prototype.removeEntry = function( entry:String ) {
if ( #IsNotMember(entry, this)) {
return this;
}
var res = #Trim( #Replace(this, entry, "") );
return (typeof res == "string" ? (res.length==0 ? [] : [res]) : res);
}
var oldArray:Array = document1.getItemValueArray("addObjName");
var myArray = oldArray.removeEntry(oldArray[index]);
document1.replaceItemValue("addObjName", myArray);
}]]></xp:this.action>
</xp:eventHandler>
</xp:image>
</xp:link>
<xp:text escape="true" id="computedField1" value="#{javascript:rowData}">
</xp:text>
</xp:panel>
</xp:repeat>
<xp:text escape="true" id="computedField2">
<xp:this.value><![CDATA[#{javascript:"and here is what was added in the link:"}]]></xp:this.value>
</xp:text>
<xp:text escape="true" id="computedField3" value="#{document1.test1}">
</xp:text>
</xp:panel>
</xp:view>
Any takers? Is there a logical explanation for this, or should I just use my work-around and stop being a pedant?
UPDATE
Using the XPage above, I can also replicate the following scenario:
Step 1: Type 'abc' in the field, select 'abcd from the choices, click on the 'add' link. All good.
Step 2: Type 'ab' then wait, then type 'c', then choose 'abce'. Add again. This adds ab, abc, abce.
Step 3: Type ab then wait, then type c, then don't choose a choice, but hit Save instead. This adds ab, abc - even though the link has not been clicked to add the value to the second field.
Step 4: Type ab, wait, type c, choose 'abcf' and click Add. Only abcf is added this time.
I'm trying to reconcile this against Sven's answer below. Partial update works, so I know he's right. In step 2 above, I'm thinking that the typeahead code is executing twice, each time triggering the onClick event for the link, and lastly the onClick event for the link is running. Similar thing in Step 3 - the link isn't being clicked at all in that example, but the onClick code is still running. In step 4, after the save, typeahead is executing twice, but this time it isn't executing the onClick code.
Now I think I get it, even if I don't like it. As Sven says, The $$xspsubmitid identifies the element which submitted the data to the server - in the last submit action. So in this case it remembers the previous submit state and repeats that - so when you are doing it immediately after a plain save, you don't get any extra code, but when you are doing it after an event in the link, it submits the page in the same way that the link did - complete with onClick code.
There's probably a reason why doing this is a good thing, but my head hurts and the problem is solved! Thanks Sven!
It is your typeahead which updates the value of your field / your array. When typing a second or more characters in the input box, the current data in your input box is sent to the server and this updates the value in the document. When clicking onto the link, the value is added again.
EDIT:
You can fix this by changing the mode of the typeahead to "partial"
EDIT 2:
The difference between the refresh modes of a typeahed is how the data are sent to the server: In case of a full refresh, the data are sent with a POST request, in case of a partial refresh, the data are sent as a GET request.
Let's have a look at the different HTTP requests:
1.) Partial mode
2.) Full mode
As you can see in the two picutrures, in full mode there are more "fields" sent to the server. The interessting one is the $$xspsubmitid: Currently it is empty. But as soon we click the link, the field is filled with the id of the link:
The $$xspsubmitid identifies the element which submitted the data to the server. In this case the link, and the server processes the code of the link.
And now comes the problem with the typeahead in full mode:
As you can see, the $$xspsubmitid is always added to the POST request, and that's why the server executes the click event of the link for each typeahead request.

Cannot get selected value of a combobox

I'm going crazy: I cannot access the selected value of a combobox in the onchange event:
<xp:comboBox id="comboBox1" value="#{sessionScope.groupBy}">
<xp:selectItem itemLabel="nothing" itemValue=""></xp:selectItem>
<xp:selectItem itemLabel="State" itemValue="state"></xp:selectItem>
<xp:selectItem itemLabel="CCType" itemValue="cctype"></xp:selectItem>
<xp:eventHandler event="onchange" submit="true" refreshMode="complete">
<xp:this.action>
<xp:executeScript>
<xp:this.script>
<![CDATA[
#{javascript:sessionScope.groupBy = getComponent("comboBox1").getValue();
print( getComponent("comboBox1").getValue() )}
]]>
</xp:this.script>
</xp:executeScript>
</xp:this.action>
</xp:eventHandler>
</xp:comboBox>
I want to store the value and reload the page to access the value I just submitted. I also tried getSubmittedValue() and value only. They always return null.
What is the trick here?
I have a problem similar to that one but if I understand your quandary I may have a solution. You are trying to capture the value of a combo box field that you select correct? Here is the code for the combo box (name: POVendor). The view I'm drawing the list from is named "PLBV".
#DbColumn(#DbName(), "PLBV", 1)
Here is the code for the Computed field that captures the value of the selection in the combo box. Just do a partial refresh on the computed field from the combo box & it should work fine.
var item = document1.getValue("POVendor");
return item;
I was facing the similar issue, tried the below option and it worked for me.
Set the Server Options on the onChange event of the combobox to Full Update and check the option "Process Data without validation
This will give you the desired result.(Your sessionScope.groupBy will be set to the new selected value of the combobox.

optgroup with Combo Box control

Is it possible to render <optgroup> tags within select by native or ExtLib Combo box control?
I want to use native solution, so getComponent() or validators will work. That disqualifies inline html with jQuery/dojo, i think.
There doesn't seem to be native support for the <optGroup> tag jet. However, in contrast to the assumption of disqualifying jQuery/dojo, does this seem to be the solution. Where getComponent(), getValue(), setValue() etc.. still work.
The only thing that needs to be done for every <optGroup> you want, is adding a <xp:selectItem itemLabel="With your optGroup label" itemValue"optGroup1"></xp:selectItem>. When multiple <optGroups>'s are used the itemValue needs to increment by 1. I pasted an example below, hope this helps.
<xp:this.resources>
<xp:script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" clientSide="true"></xp:script>
</xp:this.resources>
...
<xp:comboBox id="comboBox1" styleClass="optGroup">
<xp:selectItem itemLabel="Swedish Cars" itemValue="optGroup1"></xp:selectItem>
<xp:selectItem itemLabel="Volvo" itemValue="volvo"></xp:selectItem>
<xp:selectItem itemLabel="Saab" itemValue="saab"></xp:selectItem>
<xp:selectItem itemLabel="German Cars" itemValue="optGroup2"></xp:selectItem>
<xp:selectItem itemLabel="Mercedes" itemValue="mercedes"></xp:selectItem>
<xp:selectItem itemLabel="Audi" itemValue="audi"></xp:selectItem>
</xp:comboBox>
<xp:scriptBlock id="scriptBlock1">
<xp:this.value><![CDATA[
//Iterate across the <option>'s for which the itemValues begin with "optGroup".
$('.optGroup option[value^="optGroup"]').each(function(index, node) {
//Use the actual itemValue ("optGroup1") to get all its siblings until the next
//<option> is found with (again) an itemValue of "optGroup" (i.e. "optGroup2").
$('.optGroup option[value="' + node.value + '"]').
//No harm for last iteration: .nextAll() will be used if the selector is
//not matched or is not supplied (in this example "optGroup3" won't get a match).
nextUntil('.optGroup option[value="optGroup' + (index + 2) + '"]').
//Wrap them in a <optGroup> tag and give it its label (itemLabel).
wrapAll('<optGroup label="' + node.text + '"></optGroup>');
//Remove the initial <option> since we no longer need it.
$(this).remove();
});
]]></xp:this.value>
</xp:scriptBlock>
<xp:button value="Submit" id="button1">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:
print("Submitting: " + getComponent("comboBox1").getValue() );
}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
I have found this answer to my question.
Computed values for combo box in Domino Designer can return string vectors/arrays, usually as return value of #DbLookup or #DbColumn. While experimenting, I have found you can return native component for select items, including group (which inherits from select item).
Following snippet will build what I wanted: grouped hierarchy of items.
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:comboBox id="comboBox1" value="#{viewScope.combo}">
<xp:selectItems>
<xp:this.value><![CDATA[${javascript:var itms = new javax.faces.model.SelectItem[2];
var itm = null;
itm = new javax.faces.model.SelectItem();
itm.setLabel("label1");
itms[0] = itm;
itm = new javax.faces.model.SelectItem();
itm.setLabel("label2");
itms[1] = itm;
var g = new javax.faces.model.SelectItemGroup();
g.setLabel("Group");
g.setSelectItems(itms);
g
}]]></xp:this.value>
</xp:selectItems>
</xp:comboBox>
</xp:view>
Sample:
Based on this example you can combine many data sources to build computed <optgroup> combos, including backing beans or scoped variables.

Resources