How do I get the value of nth Edit box in a repeat control? - xpages

I have an EditBox control in a repeat control. Its iteration formula is:
return 5;
It is successfully displaying 4 edit boxes (the starting index is set to 1).
In SSJS, how can I get the value of the nth Edit Box?

You could set a sessionScope variable (or any scope variable) on the onchange event of the edit box and then in your SSJS reference the sessionScope variable. Here is some sample code, the bottom bit just shows your sessionScope variables on the page.
<?xml version="1.0" encoding="UTF-8"?>
<xp:repeat id="repeat1" rows="30" value="#{javascript:5}"
indexVar="rptIndex">
<xp:inputText id="inputText1">
<xp:eventHandler event="onchange" submit="true" refreshMode="complete">
<xp:this.action>
<xp:executeScript>
<xp:this.script><![CDATA[#{javascript:sessionScope['text'+rptIndex] = getComponent("inputText1").getValue()}]]></xp:this.script>
</xp:executeScript>
</xp:this.action></xp:eventHandler></xp:inputText>
</xp:repeat>
<xp:table styleClass="debug">
<xp:tr>
<th>Variable Name</th>
<th>Variable Content</th>
</xp:tr>
<xp:repeat id="varRepeat" rows="30" value="#{javascript:sessionScope.keySet();}" var="scopeData">
<xp:tr>
<xp:td>
<xp:text escape="true" id="varName" value="#{javascript:scopeData}" />
</xp:td>
<xp:td>
<xp:text escape="true" id="varValue" value="#{javascript:sessionScope.get(scopeData)}" />
</xp:td>
</xp:tr>
</xp:repeat>
</xp:table>
</xp:view>

When you add a submission to an onChange event you create a rather chatty application - might bite you. The solution for setting the focus is rather different. First: focus is a client side operation, so you need a client script that 'knows what control is the first failure. The good news: XPages adds to all fields that failed a server side validation the attribute aria-invalid=true.
So you can use a dojo.onLoad script that queries that and sets the focus to the first member of the result. See also http://dontpanic82.blogspot.com/2011/07/xpages-styling-invalid-field.html
And for the query syntax:
http://dojotoolkit.org/reference-guide/1.7/dojo/query.html

Repeats are fun to deal with to say the least. If you look at examples in the teamroom template mobileThread custom control you'll see a repeat for displaying a list of replies, you'll also notice a lot of javascript to go along with it as for example running script on one button click would run on all buttons in the repeat.
If you are looking for the validation problem stwissel's solution looks the best. If this is something else and at some point you just need the value of any given edit box, maybe you should think about something like:
var domEl = dojo.byId(' <repeatControlId> ');
var textBoxes = domEl.getElementsByTagName("input");
var certainValue = textBoxes[3].value;
Now certainValue contains the value of a given edit box.
haven't tried this out, might need a little tweaking but the general idea should work I would think.

added another comment so i could add code.
Did a quick test and works fine for me, see my example below. Hope it helps. Try adding some print outs to see is it getting each bit.
<xp:repeat id="TestRepeat" rows="100" var="rowData"
indexVar="commentIndex" first="0" rendered="true">
<xp:this.value><![CDATA[#{javascript:
var dataArray = new Array();
dataArray.push(" Test");
dataArray.push(" Test");
dataArray.push(" Test");
dataArray.push(" Test");
dataArray.push(" Test");
return dataArray;
}]]></xp:this.value>
<xp:panel>
<xp:label value="Test"></xp:label>
<xp:inputText id="inputText1" value="Test" defaultValue="Test">
</xp:inputText>
<xp:br></xp:br>
</xp:panel>
</xp:repeat>
<xp:button value="Test" id="button1">
<xp:eventHandler event="onclick" submit="false">
<xp:this.script>
<xp:executeClientScript>
<xp:this.script><![CDATA[
var domEl = dojo.byId('#{id:TestRepeat}');
var textBoxes = domEl.getElementsByTagName("input");
alert( "Value 1: " + textBoxes[0].value);
]]></xp:this.script>
</xp:executeClientScript>
</xp:this.script>
</xp:eventHandler>
</xp:button>

Related

How to sync edit mode with responses in xp:repeat

Notes-wise, I have a main doc and its responses documents.
XPage-wise, main doc and responses are displayed in the same xp:view, the responses being in a xp:table constructed by a xp:repeat
Each row of the table holds a panel, whose datasource is of course bound to the particular response.
So far, pretty straightforward, isn't it ?
I would like very much to ensure that, at any time, the edit mode of the main doc and the responses are in sync. Something along the lines of :
Everything is in read-only mode
User switches main doc to edit mode, automagically all responses switch to edit mode too
Ditto in reverse : switching main doc to read-only switches responses to read-only
I tried to dynamically compute the "action" attributes of the data sources in the rows, but that does not seem to work.
I'm thinking of somehow having the responses listen to the event "main doc changes mode" and acting accordingly, but I can't seem to be able to wrap my head around how to do it. Specifically, which event would that be ? Then, how to have the responses listen to an event happening somewhere else ?
Any hint appreciated
thx
edit : back a few days later, if anyone still there.
As per stwissel's suggestion, here be code :
<?xml version="1.0" encoding="UTF-8"?>
<xp:view
xmlns:xp="http://www.ibm.com/xsp/core"
xmlns:xe="http://www.ibm.com/xsp/coreex"
style="background-color:rgb(217,217,255)">
<xp:this.data>
<xp:dominoView
var="subdocsNotesView"
viewName="#{javascript:compositeData.subdocsNotesView}"
categoryFilter="#{javascript:compositeData.ctnrK}">
</xp:dominoView>
</xp:this.data>
<xp:table>
<xp:repeat
id="leRepeat"
value="#{subdocsNotesView}"
var="oneDoc">
<xp:tr>
<xp:panel id="rowPanel">
<xp:this.data>
<xp:dominoDocument
var="rowData"
formName="Person"
documentId="#{javascript:oneDoc.getDocument().getUniversalID()}"
action="editDocument">
</xp:dominoDocument>
</xp:this.data>
<xp:td>
<xp:inputText
id="Name"
value="#{rowData.Name}"
defaultValue="#{javascript:oneDoc.getDocument().getItemValueString('Name')}">
</xp:inputText>
</xp:td>
</xp:panel>
</xp:tr>
</xp:repeat>
</xp:table>
</xp:view>
Surround your repeat control with a panel.
Calculate panel's property readonly depending on main document's read or edit mode.
Use a boolean viewScope variable "editMode" to memorize the current mode.
<xp:table>
<xp:panel readonly="#{ not viewScope.editMode}">
<xp:repeat ...>
...
</xp:repeat>
</xp:panel>
</xp:table>
Change mode in buttons:
<xp:button value="Editmode" id="button1">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action>
<xp:actionGroup>
<xp:changeDocumentMode mode="edit" var="mainDocument">
</xp:changeDocumentMode>
<xp:executeScript>
<xp:this.script><![CDATA[#{javascript:
viewScope.editMode = true
}]]></xp:this.script>
</xp:executeScript>
</xp:actionGroup>
</xp:this.action>
</xp:eventHandler>
</xp:button>
<xp:button value="Readmode" id="button2">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action>
<xp:actionGroup>
<xp:changeDocumentMode mode="readOnly" var="mainDocument">
</xp:changeDocumentMode>
<xp:executeScript>
<xp:this.script><![CDATA[#{javascript:
viewScope.editMode = false
}]]></xp:this.script>
</xp:actionGroup>
</xp:this.action>
</xp:eventHandler>
</xp:button>
Even though repeat's dominoDocument has always property action="editDocument" repeat documents are in read mode when panel's property readonly is true.
If panel's property readonly is false then repeat documents are in edit mode.
This way edit mode of main document and repeat documents is in sync.
I would recommend using a Managed Bean which implements the Model View Controller (MVC). The Controller Bean in the view scope contains the current Main Doc model and coordinates access to the main doc and its responses. The Main Doc Model would include an array of the Response Doc Model so that the read mode is managed at the Controller while the data is managed in the model.
John Dalsgaard has an excellent presentation on this topic located here.
I use this approach for both Parent/Child documents and for documents that contain items which are related or synced lists.
Happy coding.

How to do a partial refresh of multiple items in Xpages correctly

I have read many of the blog posts on doing a partial refresh on more than one element, but I cannot seem to get it to work.
I made a "toy" example, an Xpage with two fields and one button. Both fields are bound to a sessionScope counter. The button increments the counter and does a partial refresh on the containing panel.
I want to do a partial refresh on the first field, which is not in the panel [in my real Xpage the two fields are very far apart on the form.
Tried many different things to get this to work, but none worked. Is it even possible to do this?
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
xmlns:xe="http://www.ibm.com/xsp/coreex"
xmlns:xc="http://www.ibm.com/xsp/custom"
style="background-position:center left"
xmlns:on="http://www.openntf.org/domino/xsp" viewState="nostate"
xmlns:debug="http://www.openntf.org/xsp/debugtoolbar">
First Var<br></br>
<xp:text escape="true" id="computedField1" value="#{sessionScope.number}">
</xp:text>
<br></br>
<xp:br></xp:br>
<br></br>
Second Var<xp:panel id="pnl1">
<xp:text escape="true" id="computedField2" value="#{sessionScope.number}">
</xp:text>
<br></br>
<br></br>
<xp:button value="Label" id="button1">
<xp:eventHandler event="onclick" submit="true"
refreshMode="partial" refreshId="pnl1" execMode="partial"
execId="pnl1">
<xp:this.action><![CDATA[#{javascript:var t = sessionScope.number + 1;
sessionScope.number = t;}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
</xp:panel>
</xp:view>
Read Knut's link and got this to work. I made a change to the event handler of the button and it worked. Here is the code:
<xp:eventHandler event="onclick" submit="true"
refreshMode="partial" refreshId="pnl1" execMode="partial"
execId="pnl1">
<xp:this.action><![CDATA[#{javascript:var t = sessionScope.number + 1;
sessionScope.number = t;}]]></xp:this.action>
<xp:this.onComplete><![CDATA[XSP.partialRefreshPost("#{id:computedField1}");]]></xp:this.onComplete>
</xp:eventHandler>
Use XSP.partialRefreshPost(): http://avatar.red-pill.mobi/tim/blog.nsf/d6plinks/TTRY-84B6VP

Teamstudio Unplugged: Inline edit

I'm using teamstudio's unplugged for the record.
I'm trying to build a table using a repeat control with a view as a data source.
The user should be able to change edit the documents and then save all data sources.
Below is a example that currently does not work.
I have also tried using computed ids for all the different check-boxes in the cells and
storing the UNID of the row's document in a hidden text field and then read every row based on the computed ids and saving them. That didn't work because computing ids in a repeater is impossible in unplugged despite tons of effort.
If i have done something wrong or there is a trick i haven't tried, i would love to hear about it.
<xp:table id="table">
<xp:tr>
<xp:td>Document name</xp:td>
<xp:td>Field1</xp:td>
<xp:td>Field2</xp:td>
<xp:td>Field3</xp:td>
</xp:tr>
<xp:repeat id="repeat1" rows="30"
value="#{TestDocs}" indexVar="index" var="Docs" first="0"
removeRepeat="true" repeatControls="true">
<xp:panel>
<xp:tr>
<xp:td>
<xp:text id="Name" escape="true"
value="#{document1.Name}">
</xp:text>
</xp:td>
<xp:td>
<xp:checkBox id="Field1"
checkedValue="true" uncheckedValue="false"
value="#{document1.Field1}">
<xp:this.defaultChecked><![CDATA[${javascript:
if(document1.getDocument().getItemValue("Field1")=="[true]"){
return true;
}else{
return false;
}}]]></xp:this.defaultChecked>
</xp:checkBox>
</xp:td>
<xp:td>
<xp:checkBox id="Field2"
checkedValue="true" uncheckedValue="false"
value="#{document1.Field2}">
<xp:this.defaultChecked><![CDATA[${javascript:
if(document1.getDocument().getItemValue("Field2")=="[true]"){
return true;
}else{
return false;
}}]]></xp:this.defaultChecked>
</xp:checkBox>
</xp:td>
<xp:td>
<xp:checkBox id="Field3"
checkedValue="true" uncheckedValue="false"
value="#{document1.Field3}">
<xp:this.defaultChecked><![CDATA[${javascript:
if(document1.getDocument().getItemValue("Field3")=="[true]"){
return true;
}else{
return false;
}}]]></xp:this.defaultChecked>
</xp:checkBox>
</xp:td>
</xp:tr>
</xp:panel>
</xp:repeat>
</xp:table>
<xp:button value="Save" id="button3"
styleClass="button">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete" disableValidators="true">
<xp:this.action>
<xp:save></xp:save>
</xp:this.action>
</xp:eventHandler>
</xp:button>
My inclination would be to move the logic for all of this client side. So build the table with jQuery, and then on save, take the values out of the fields you create and write them into hidden fields that will actually get saved.
In terms of computing IDs of fields etc, I would add attributes to the elements you want to work with and then select them using jQuery:
$('[myattr="the value"]')

Getting values from repeat control

I have a field inside a repeat control. Right now am using a viewScope in the onChange event to capture the field values inside the repeat control. Each time the field has to partial refreshes in order to get the value set in the scope variable.
The problem here is when the users set the focus outside the repeat control, the focus is not set until the partial refresh of the field is completed. Sometimes this partial refresh is too slow when user is accessing a remote domino server. Is there a effective way to capture the values inside the repeat control when the form is submitted?
The idea is to create fields dynamically when user click on the add button. The problem happen when you move the focus from the field inside the repeat control to the field outside the repeat control. The focus is not set, until the partial refresh of field inside the repeat control is completed. You may not occur this scenario, if the server is running locally on your machine. The below code shows the usage of repeat control to create fields dynamically
<xp:button value="Add Objects" id="addNavObj">
<xp:eventHandler event="onclick" submit="true"
refreshId="objLine" refreshMode="partial" id="eventHandler24">
<xp:this.action><![CDATA[#{javascript:viewScope.rowItems=viewScope.rowItems+1;getComponent("navObjRep").setValue(parseInt(viewScope.rowItems));}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
<xp:repeat rows="30" var="rowData" indexVar="rowIndex"
repeatControls="false" first="0" value="#{javascript:viewScope.rowItems}"
id="navObjRep" style="width:800.0px">
<xp:panel style="width:800.0px" id="objLine">
<xp:table style="width:800.0px">
<xp:tr>
<xp:td style="width:245.0px">
<xp:inputText id="objType" style="width:130.0px">
<xp:typeAhead mode="full" minChars="1" ignoreCase="true"
id="typeAhead4" rendered="false">
</xp:typeAhead>
</xp:inputText>
</xp:td>
<xp:td style="width:46.0px">
<xp:inputText id="objCode">
<xp:eventHandler event="onchange" submit="true"
refreshMode="partial" refreshId="objCode" id="eventHandler3">
<xp:this.action><![CDATA[#{javascript:viewScope['objCode'+rowIndex] = getComponent("objCode").getValue()}]]></xp:this.action>
</xp:eventHandler>
</xp:inputText>
</xp:td>
<xp:td style="width:300.0px">
<xp:inputTextarea id="objDesc" style="height:40.0px;width:200.0px">
<xp:eventHandler event="onclick" submit="false" id="eventHandler40"></xp:eventHandler>
<xp:eventHandler event="onchange" submit="true"
refreshMode="partial" refreshId="objDesc" id="eventHandler4">
<xp:this.action><![CDATA[#{javascript:viewScope['objDesc'+rowIndex] = getComponent("objDesc").getValue()}]]></xp:this.action>
</xp:eventHandler>
</xp:inputTextarea>
</xp:td>
</xp:tr>
</xp:table>
</xp:panel>
</xp:repeat>
Don't use the onChange event of an edit box for a partial refresh - you create an experience night mare. Excercise 23 has all you need.
Its just a simple, example for a repeat control with a variable number of input and computed Fields.Hope it helps you to solve your problem.
<xp:this.beforePageLoad><![CDATA[#{javascript:var languages:java.util.Vector = #Explode("de,en,pl",",");
viewScope.put("allLanguages",languages);
viewScope.put("selectedLanguages", languages)}]]>
</xp:this.beforePageLoad>
<xp:checkBoxGroup id="checkBoxGroup1" value="#{viewScope.selectedLanguages}">
<xp:this.defaultValue><![CDATA[#{javascript:return viewScope.get( "allLanguages" );}]]></xp:this.defaultValue>
<xp:eventHandler event="onchange" submit="true" refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:// full update //partial update}]]></xp:this.action>
</xp:eventHandler>
<xp:selectItems>
<xp:this.value><![CDATA[#{javascript:return viewScope.get( "allLanguages" );}]]></xp:this.value>
</xp:selectItems>
</xp:checkBoxGroup>
<xp:repeat id="repeat1" rows="30" var="varcollection" repeatControls="true">
<xp:this.value><![CDATA[#{javascript:return viewScope.get( "allLanguages" );}]]></xp:this.value>
<xp:span>
<xp:this.rendered><![CDATA[#{javascript:var vec:java.util.Vector = viewScope.get( "selectedLanguages" );
return #IsMember(varcollection,vec);
}]]></xp:this.rendered>
<xp:label id="label1">
<xp:this.value><![CDATA[#{javascript:return varcollection + ": ";}]]></xp:this.value>
</xp:label>
<xp:inputText id="inputText1" loaded="true">
<xp:this.value><![CDATA[${javascript:var fieldName = "Help_" + varcollection;
return '#{viewScope.' + fieldName + '}';}]]></xp:this.value>
<xp:eventHandler event="onchange" submit="true"
refreshMode="partial" refreshId="computedField1">
</xp:eventHandler></xp:inputText>
  <xp:text escape="true" id="computedField4"><xp:this.value><![CDATA[#{javascript:var fieldName = "Help_" + varcollection;
return '#{viewScope.' + fieldName + '}';}]]></xp:this.value></xp:text>  
<xp:text escape="true" id="computedField1">
<xp:this.value><![CDATA[${javascript:var fieldName = "Help_" + varcollection;
return '#{viewScope.' + fieldName + '}';
}]]></xp:this.value>
</xp:text>
<xp:br></xp:br>
</xp:span>
</xp:repeat> <xp:br></xp:br>
<xp:button value="Submit" id="button1">
<xp:eventHandler event="onclick" submit="true" refreshMode="complete" immediate="false" save="true"></xp:eventHandler></xp:button>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:text escape="true" id="computedField3"><xp:this.value><![CDATA[#{javascript:return "value1 = " +viewScope["Help_de"] +
"value2 = " +viewScope["Help_en"] +
"value3 = " +viewScope["Help_pl"]}]]></xp:this.value></xp:text>
The last computet field: computetdField3 will get the values 'onSubmit' and the others onChange.
I strugled for a few hours with about the same problem.
I have a repeat build up from different views and even do some consolidation on the result to show a nice input matrix of 3 field per row.
I want to create new documents for every line that has fields filled in in the repeat.
Partial refresh is indeed a nightmare.
Finaly I ended up with client side script onfocus and onChange events to add my data into a hidden field on the page. I concatenate the 3 fields per row and put them in the hidden field seperated by a §.
When pressing the submit button I just get server side the value of that hidden field, #Explode("§") it and run trough the newly created array and create documents from them.

Xpages Dynamic dojo dialog control

I have a list of products (created using a repeat control) and wish to click on a particular product and bring up a dialog with further information about that particular product. I don't really want to generate dijit.dialog thing for every single product on that page, so how can I do this dynamically possibly using AJAX and partial refresh.
A similar non xpages example can be seen here: http://www.replacementkeys.co.uk/window?dir=asc&limit=12&mode=grid&order=position - where you hover over an image and a quick view button comes up, which then dynamically loads the content for that product.
Any ideas would be truly appreciated.
We build the dialog outside the repeat control and then the action that launches or shows it also sets a viewScope variable that is used UNID for the data source in the dialog. Just make sure you refresh the contents of the dialog as you open it...
<xp:view xmlns:xp="http://www.ibm.com/xsp/core" xmlns:xe="http://www.ibm.com/xsp/coreex">
<xp:this.data>
<xp:dominoView var="promptView" viewName="dlgBoxes">
</xp:dominoView>
</xp:this.data>
<xp:panel>
<xp:repeat id="repeat1" rows="30" value="#{promptView}" var="promptEntry">
<xp:panel tagName="div">
<xp:text escape="true" id="computedField1" value="#{promptEntry.dlgName}">
</xp:text>
 
<xp:button value="details" id="button1">
<xp:eventHandler event="onclick" submit="true" refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:
var pe:NotesViewEntry = promptEntry;
viewScope.put("dlgDocUnid", pe.getUniversalID());
getComponent("dialog1").show();
}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
</xp:panel>
</xp:repeat>
</xp:panel>
<xe:dialog id="dialog1" keepComponents="false" partialRefresh="true">
<xe:this.title><![CDATA[#{javascript:
var unid = viewScope.get("dlgDocUnid");
if(!unid) return "";
var doc:NotesDocument = database.getDocumentByUNID(unid);
return doc.getItemValueString("dlgName");}]]></xe:this.title>
<xp:panel>
<xp:this.data>
<xp:dominoDocument var="dlgDoc" formName="dlgBox" action="openDocument">
<xp:this.documentId><![CDATA[#{javascript:viewScope.get("dlgDocUnid");}]]></xp:this.documentId>
</xp:dominoDocument>
</xp:this.data>
<xp:text escape="true" id="computedField2" value="#{dlgDoc.Title}">
</xp:text>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:text escape="true" id="computedField3" value="#{dlgDoc.dlg}">
</xp:text>
</xp:panel>
</xe:dialog>
</xp:view>
Happy coding
/Newbs
You can combine your repeat control with the Extension Library dialog control in order to be able to launch a dialog when the user clicks on the individual row. Chris Toohey has created an excellent article called Popup Dialog Forms from Views in XPages that demonstrates this.

Resources