I've got a checkBoxGroup that can expand to 20 items or so, as users pick which fields from a view that they want to export to a spreadsheet. I'm curious how I might control the display of those 20 checkboxes (dynamically determined) to be in more than one row or even to display in a column instead.
<xp:checkBoxGroup id="fieldChoicesBox">
<xp:eventHandler event="onclick" submit="false" id="eventHandler2">
<xp:this.script>
<![CDATA[var x= '#{javascript:getClientId("fieldChoicesBox")}';
var y= '#{javascript:getClientId("fieldChoicesBoxList")}';
copyRadioChoices(x,y)]]>
</xp:this.script>
</xp:eventHandler>
<xp:selectItems>
<xp:this.value>
<![CDATA[#{javascript: var viewName=#UpperCase(getComponent("viewChoice").getValue());
var tmp = #DbLookup(#DbName(),"dbprofile",viewName,"Value");
#If(#IsError(tmp),"None",tmp)}]]>
</xp:this.value>
</xp:selectItems>
</xp:checkBoxGroup>
This is all built off code I originally got from Russ Maher (see http://xpagetips.blogspot.com/2012/06/extending-your-xpages-applications-with.html) so any brilliance is attributable to him, while all mistakes are mine.
Alternatively, if you have an idea for how to pick fields or columns to display in the export that works more easily or elegantly, I'd be thrilled to hear it.
If your using or can use the ExtLib I'd use the valuePicker control, lets you select multiple values from the one control ( hold down ctrl while choosing ). Heres an example:
<xp:inputtext id="example" multipleSeparator=","></xp:inputText>
<xe:valuePicker for="example" pickerText="text">
<xe:this.dataProvider>
<xe:simpleValuePicker valueList="test1, test2, test3, test4"
valueListSeperator=",">
</xe:simpleValuePicker>
</xe:this.dataProvider>
</xe:valuePicker>
Related
I need to create 3 fields in one row every time a new objective is needed. objective1, midYear1, endYear1. Then if I add an objective, objective2, midYear2, endYear2 and so on. Everything seems to work, first time, but second time it creates loads of fields. I assume its the way I'm nesting / not nesting/using my repeats correctly as my viewScope variables are all correct, so it's just displaying of the fields that I'm confusing myself with. I just need each of the 3 fields in one column each, then new row and repeat.....Code below, however am also open to suggestions if anyone has a better approach..... Thanks
<xp:this.data>
<xp:dominoDocument
var="document1"
formName="objective">
</xp:dominoDocument>
</xp:this.data>
<xp:repeat id="repeat1" rows="100" value="#{viewScope.fields}"
var="fieldName">
<xp:repeat id="repeat2" rows="100" value="#{viewScope.fields2}"
var="fieldName2">
<xp:repeat id="repeat3" rows="100" value="#{viewScope.fields3}"
var="fieldName3">
<div class="row">
<div class="col-xs-4">
<xp:label value="#{fieldName}" for="inputText1">
</xp:label>
<xp:inputText id="inputText1">
<xp:this.value><![CDATA[#{document1[fieldName]}]]></xp:this.value>
</xp:inputText>
</div>
<div class="col-xs-4">
<xp:label value="#{fieldName2}" for="inputText2">
</xp:label>
<xp:inputText id="inputText2">
<xp:this.value><![CDATA[#{document1[fieldName2]}]]></xp:this.value>
</xp:inputText>
</div>
<div class="col-xs-4">
<xp:label value="#{fieldName3}" for="inputText3">
</xp:label>
<xp:inputText id="inputText3">
<xp:this.value><![CDATA[#{document1[fieldName3]}]]></xp:this.value>
</xp:inputText>
</div>
</div>
</xp:repeat>
</xp:repeat>
</xp:repeat>
<xp:button
value="Add Objective"
id="button1">
<xp:eventHandler event="onclick" submit="true" refreshMode="partial"
refreshId="repeat1">
<xp:this.action><![CDATA[#{javascript:
if (!viewScope.fields) {
viewScope.fields = [];
viewScope.fields2 = [];
viewScope.fields3 = [];
var count:integer = 1;
}
viewScope.fields.push("Objective" + (viewScope.fields.length + 1));
viewScope.fields2.push("MidYear" + (viewScope.fields2.length + 1));
viewScope.fields3.push("EndYear" + (viewScope.fields3.length + 1));
count = count+1;
}]]></xp:this.action>
</xp:eventHandler>
The problem is that by nesting, for each entry in coumn 1, you're creating n instances of column 2. Then for each entry in column, you're creating n instances of column 3. If you want this approach, you're best off creating a single viewScope variable, where each element has the fieldnames for each objective (as an object or array), then just using a single repeat using that single viewScope variable.
Building this for Notes Client / traditional Domino web was historically a huge pain because the restriction was you could not edit multiple documents (easily) in the same UI, because the Form was both the schema for storage and the design for the UI.
XPages means that's no longer a restriction. The XPage is the UI, (dominoDocument / bean) data sources are the schema for storage.
As a result, my preferred approach is to break the structure for storage up into more granular elements. So each Objective would be its own document, with fixed bindings to "objective", "midYear", "endYear".
There are then a variety of approaches for creating a new entry. One is a single fixed row at the top with a dominoDocument datasource with scope="request" ignoreRequestParams="true". Save can then add the NoteID / UNID to a repeat that then retrieves the values from the relevant document(s) to display / edit.
A more advanced approach would be to have a viewScope variable with an additional option (at beginning or end), then compute the dominoDocument datasource that's within the repeat to either retrieve the relevant document (i.e. action="openDocument") or create a new one (i.e. action="newDocument").
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.
I have 2 edit Boxes, 1 is editable, the other just prints the name of the sessionScope, both fields are in a in 2 cells next to each other , see code below, inputText1 is a editable text field, inputText2 is non editable text, printing #{sessionScope.Tmp1_ABC}
<xp:td>
<xp:inputText id="inputText1"
loaded="true">
<xp:this.value><![CDATA[${javascript:var fieldName = "Tmp_" + #ReplaceSubstring(varcollection," ","");
return '#{sessionScope.' + #ReplaceSubstring(fieldName,#List(" ",")","("),"") + '}';}]]></xp:this.value>
<xp:eventHandler
event="onchange" submit="true" refreshMode="partial"
refreshId="panel_1" />
</xp:inputText>
</xp:td>
<xp:td>
<xp:inputText id="inputText2"
loaded="true">
<xp:this.value><![CDATA[#{javascript:var fieldName = "Tmp1_" + #ReplaceSubstring(varcollection," ","");
return '#{sessionScope.' + #ReplaceSubstring(fieldName,#List(" ",")","("),"") + '}';}]]></xp:this.value>
<xp:eventHandler
event="onchange" submit="true" refreshMode="partial"
refreshId="panel_1" />
</xp:inputText>
</xp:td>
AFAIK you can't assemble your data binding that way. Data binding is an EL expression, not SSJS. You could try to trick using ${} to compute #{}
As per Stefan, using SSJS (javascript:) to define a value for an input control results in it only being read-only. For values to be editable they must be bound using Expression Language (EL). For something as complex as the example (looping over a list of field/variable) you will most likely have to learn a little Java to allow you to connect your input controls to the bean via EL. If the purpose of the code is to have one field editable and the other read-only you should (as Per suggested) change the control from being an inputText to just (computed) text or a label and remove the event handler. Somebody reading the code could easily be confused in thinking you intended both to be editable.
I'm using a the extension library value picker to select a Name. Once the user hits Ok, I need to use the selected value to populate several other fields. But I can't figure out how to fire a SSJS function from the Ok button.
Thanks for any suggestions.
-- Jeff
You can fire an event from the field that the value picker updates.
Here's a simple example that updates another field when the value picker is used:
<xe:djextListTextBox id="inputField">
<xp:eventHandler event="onChange" submit="true" refreshMode="complete">
<xe:this.action><![CDATA[#{javascript:getComponent("testField").setValue(getComponent("inputField").getValue())}]]></xe:this.action>
</xp:eventHandler>
</xe:djextListTextBox>
<xe:valuePicker id="valuePicker1" for="inputField">
<xe:this.dataProvider>
<xe:simpleValuePicker valueList="1,2,3" valueListSeparator=","></xe:simpleValuePicker>
</xe:this.dataProvider>
</xe:valuePicker>
<xp:br />
<xp:inputText id="testField"></xp:inputText>
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.