Data handling with nested repeat controls - xpages

In my Application I, as a user, can create "items" through an XPage.
Those items have an undefined amount of entries in a list (a simple text list) which I create myself. Those entries will be used to generate checkBoxes later.
I have a list of customers. I can add multiple items to each customer. The items are displayed inside a repeat control. Each row has an inner repeat control for the checkBoxes (generated from the text list).
Now my problem is, that I'm not sure how to handle the data or more precisely the checking/unchecking of the checkBoxes because I have an undefined number of items and an undefined number of the checkBoxes inside the items plus the checkBoxes are editable.
I hope my issue is clear. Please let me know if there is need for clarification or if you need some code - even though I think the problem lies purely with the logic in my head.
EDIT:
The custom control that is used in the customer XPage the checkBoxes I mentioned above are used for `status.
As you will see below I simply get the search keys for the items and use them to find the text lists to create the checkboxes.
Since the items are merely templates that are used for every user that logs in, I can't use those documents to save the state of the checkboxes.
The only thing I can think of at this moment is to actually create a document for every item that is being added to the customer and use this document as the storage - but isn't there a better way?
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:this.resources>
<xp:styleSheet href="/item.css"></xp:styleSheet>
</xp:this.resources>
<xp:repeat id="repeat1" rows="30" var="rowData" indexVar="rowIndex" style="border: 1px solid #ccc;">
<xp:this.value>
<![CDATA[#{javascript:return currentForm.getItemValue("itemSearchkey");}]]>
</xp:this.value>
<xp:div>
<xp:button styleClass="accordion" id="button1">
<xp:this.value>
<![CDATA[#{javascript:
var itemTitle = "";
if(!"".equals(rowData) || rowData != null) {
itemTitle = itemBean.getItemTitle(rowData);
}
return itemTitle;
}]]>
</xp:this.value>
<xp:eventHandler event="onclick" submit="false">
<xp:this.script>
<![CDATA[
// ... some code for the accordion to work
]]>
</xp:this.script>
</xp:eventHandler>
</xp:button>
<xp:div styleClass="panel">
<xp:repeat id="repeat2" rows="30" var="rowDataStatus" indexVar="rowIndexStatus">
<xp:this.value>
<![CDATA[#{javascript:
var statusVector = null;
if(!"".equals(rowData) || rowData != null) {
statusVector = itemBean.getStatusList(rowData);
}
return statusVector;
}]]>
</xp:this.value>
<xp:checkBox id="checkBox1" checkedValue="true" uncheckedValue="false">
<xp:this.text><![CDATA[#{javascript:return rowDataStatus;}]]></xp:this.text>
</xp:checkBox>
</xp:repeat>
</xp:div>
</xp:div>
</xp:repeat>
</xp:view>
To create the list that is used for the checkBoxes I use a simple inputTextArea in a separate custom control. currentForm is the datasource:
...
<xp:inputTextarea id="statusList" value="#{currentForm.StatusList}"
multipleSeparator="#{javascript:#NewLine()}" rows="8">
</xp:inputTextarea>
...

Related

Is it possible to have data binding in the view?

From this post, thank you Simon Delicata's answer and comment. I can save get selected documents from a view and save as separate documents.
I have a question about this approach. If I edit the document, I click the link which will redirect me to the edit page, the edit page is similar to previous post's design: a combo box on the top, a view with check box (this view is used for selection), a button for save the selection values from the view and the combo box. Also there will another view which will display the saved values.
Here code and it can save as separate documents but it cannot show the saved value and put a "tick" symbol in the checkbox.
<xp:table id="table1a">
<xp:tr>
<xp:td id="table1">
<xp:comboBox id="comboBox1"
dojoType="dijit.form.ComboBox" style="width:250.0px"
value="#{document1.Category}">
<xp:selectItems id="selectItems3">
<xp:this.value><![CDATA[#{javascript:var SetFirstValueBlank = #Text("");
return SetFirstValueBlank;
}]]></xp:this.value>
</xp:selectItems>
<xp:selectItems id="selectItems4">
<xp:this.value><![CDATA[#{javascript:#Unique(#DbColumn(#DbName(), "CategoryListView", 1));}]]></xp:this.value>
</xp:selectItems>
<xp:eventHandler event="onchange" submit="true"
refreshMode="partial" refreshId="table1a">
</xp:eventHandler>
</xp:comboBox>
<xp:text escape="true" id="computedField1">
</xp:text>
</xp:td>
</xp:tr>
<xp:tr>
<xp:td>
<xp:viewPanel rows="10" id="viewPanel1" var="rowData">
<xp:this.data>
<xp:dominoView var="view1"
viewName="hListView">
</xp:dominoView>
</xp:this.data>
<xp:viewColumn columnName="ItemName"
id="viewColumn1" style="background-color:rgb(255,255,255)"
showCheckbox="true">
<xp:viewColumnHeader value="Item Name"
id="viewColumnHeader1" rendered="false">
</xp:viewColumnHeader>
</xp:viewColumn>
<xp:this.facets>
<xp:pager partialRefresh="true"
layout="Previous Group Next" xp:key="footerPager" id="pager2">
</xp:pager>
</xp:this.facets>
</xp:viewPanel>
<xp:br></xp:br>
<xp:button value="Add" id="button1"
style="height:35.0px">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:var Category = getComponent("comboBox1").getValue();
var viewPanel=getComponent("viewPanel1");
var docIDArray=viewPanel.getSelectedIds();
var unidArray = new Array();
for(i=0; i < docIDArray.length; i++) {
var unid=database.getDocumentByID(docIDArray[i]).getUniversalID();
unidArray.push(unid);
}
viewScope.put("unidArray", #Implode(unidArray, ","));
for(var i=0; i< unidArray.length;i++ )
{
var mdoc = database.createDocument();
mdoc.appendItemValue("Form", "ItemForm");
mdoc.appendItemValue("ItemName",unidArray[i]);
if(mdoc.computeWithForm(false,false))
{
mdoc.save(true, true);
getComponent("computedField9").setValue("Record saved");
}
else
{
}
}
}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:viewPanel rows="6" id="viewPanel2">
<xp:this.data>
<xp:dominoView var="view2" viewName="CategoryItemView">
<xp:this.categoryFilter><![CDATA[#{javascript:getComponent("comboBox1").getValue();}]]></xp:this.categoryFilter>
</xp:dominoView>
</xp:this.data>
<xp:this.rendered><![CDATA[#{javascript:var value = getComponent("comboBox1").getValue();
if(value =="" || value == null)
{
return false;
}
else
{
return true;
}}]]></xp:this.rendered><xp:viewColumn id="viewColumn4" columnName="Category" rendered="false">
<xp:this.facets>
<xp:viewColumnHeader xp:key="header" id="viewColumnHeader4" value="Category">
</xp:viewColumnHeader>
</xp:this.facets>
</xp:viewColumn>
<xp:viewColumn id="viewColumn3" columnName="$10">
<xp:this.facets>
<xp:viewColumnHeader xp:key="header" id="viewColumnHeader3" value="Category">
</xp:viewColumnHeader>
</xp:this.facets>
</xp:viewColumn>
<xp:viewColumn columnName="ItemName" id="viewColumn2">
<xp:viewColumnHeader value="Item" id="viewColumnHeader2">
</xp:viewColumnHeader>
</xp:viewColumn>
<xp:this.facets>
<xp:pager partialRefresh="true" layout="Previous Group Next" xp:key="footerPager" id="pager3">
</xp:pager>
</xp:this.facets></xp:viewPanel><xp:br></xp:br></xp:td>
</xp:tr>
</xp:table>
So far as I know, suppose there a edit box or a combo box, if I use data binding to bind to a field and save it, the a edit box or a combo box can show the saved value when edit.
So I would like to know can the "similar approach" applies to the view? I search on the Internet and not much information about it.
I click the view and click Data in Properties tab, I notice the view does has data binding to bind to a form field.
Therefore is it possible to have data binding in the view so the view can show the saved value when edit. Thank you.
You're not binding to a document, you're binding to properties in a dominoDocument object. The dominoDocument object is a Java wrapper for the underlying document in the database that, because the document is read-write also exposes all fields as read-write, allowing editable components to be bound to them. If an edit box was bound to a read only property, I suspect it would not show as editable.
The dominoView object is a Java wrapper for the underlying view, and returns a collection of NotesXspViewEntry objects i.e. each row in the view. Those aren't read-write (Notes Client editable columns don't edit the view entry, they give an editable area that writes to the underlying document via backend code). You need to think about binding to "documents corresponding to view entries in the view".
View Panel columns can contain components instead of just mapping to column values. A View Panel is a repeat control with restricted layout and settings, so it's the same approach. Set the var property on the ViewPanel - this is a variable to get a handle on the current NotesXspViewEntry. You can't drag and drop into a column, if I remember correctly, you need to paste the code for the component into the relevant place.
If you want it editable, you'll need a dominoDocument datasource. Alternatively, reproduce the approach of Notes Client editable view column approach, bind to a temporary variable (e.g. requestScope property) and update the backend document on completion of the process. Another approach is to launch a dialog, passing the current row's UNID, and edit. Yet another approach is to use the In Place Form from XPages Extension Library.
Here's a presentation and demo from IBM Connect 2013 where I do something similar to make a column editable http://www.intec.co.uk/session-slides-and-sample-database-from-ibm-connect/.
More modern view components like the Data View make it easier.

duplicate items in Document after saving in a bean

I experience strange but reproduceable behaviour with an XPage which saves its values through a java bean. After copyAllItems to an document there are two richtext items in the document. First is empty, second is filled as expected.
This is my Xpage:
<?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="docDataSource" formName="test"></xp:dominoDocument>
</xp:this.data>
<xp:div id="test">
<xp:fileUpload id="fileUpload1" value="#{docDataSource.test}"></xp:fileUpload>
</xp:div>
<xp:button value="Label" id="button1">
<xp:eventHandler event="onclick" submit="true"
refreshMode="partial" refreshId="test">
<xp:this.action><![CDATA[#{javascript:registration.testCopyAllItems(docDataSource);}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
This is my java bean method:
public void testCopyAllItems(DominoDocument docDataSource) throws NotesException{
Document docUser = database.createDocument(); // <- get any database
docDataSource.getDocument(true).copyAllItems(docUser, true);
docUser.save();
}
This is the result in the document:
Does anyone have a hint on what could cause the trouble?
This seems to be a "normal" behaviour and I have seen it a lot working with RichtText fields. It shouldn't matter. Notes can deal with a RichText field constisting of more than one item.
As a workaround,
delete the RichText field after copyAllItems() with removeItem() and
copy it with copyItem() separately.
This should result in one item only.

Trying to get the value of a listBox with Select2

I have the latest extension library installed running on the latest version of Domino. I am using a simple listBox with values from a DBColumn ( which works to populate the listBox ).
However, I can't seem to get the selected value of the listBox. I've poked around the net a while and found several different things which I have tried unsuccessfully. Then I started reading about having to install other libraries etc. Now I am confused. Below is my code. All I want to do is get the selected value from the listBox once it changes but I really want to use the select2 features to search the listBox. Can someone point me in the right direction on how to get the selected value when it changes?
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core" xmlns:bx="http://www.openntf.org/xsp/bootstrap">
<xp:scriptBlock id="scriptBlock2">
<xp:this.value>
<![CDATA[
$(document).ready(
function() { x$( "#{id:listBoxProperties}" ).select2()
.on("change", function(e) { XSP.partialRefreshPost(
"#{id:computedField2}" );
}
}
);
]]>
</xp:this.value>
</xp:scriptBlock>
<xp:panel>
<xp:listBox id="listBoxProperties" value="#{viewScope.selectedProperty}"
style="width:250px">
<xp:selectItems>
<xp:this.value><![CDATA[#{javascript:listOfProperties = #DbColumn( #DbName(), 'vwAuditDocsByPropertyNo', 1 );
if( #IsError( listOfProperties ) )
"Error looking up properties: " + listOfProperties;
listOfProperties;
}]]></xp:this.value>
</xp:selectItems>
<xp:eventHandler event="onchange" submit="true"
refreshMode="complete">
</xp:eventHandler></xp:listBox>
<bx:select2PickerCombo id="select2Property"
for="listBoxProperties" placeHolder="-Select a Property-"
binding="#{javascript:viewScope.selectedProperty}">
</bx:select2PickerCombo>
<xp:br></xp:br>
<xp:text escape="true" id="computedField1"
value="#{javascript:viewScope.selectedProperty;}">
</xp:text>
<xp:text escape="true" id="computedField2">
<xp:this.value><![CDATA[#{javascript:getComponent( "listBoxProperties").getValue()}]]></xp:this.value>
</xp:text>
</xp:panel>
</xp:view>
I have tested your scenario and can verify that the onchange event of the lixtBox does not fire when using bx:select2PickerCombo to style the listBox as a Select2 control. If you remove the use of bx:select2PickerCombo, the onchange event fires as expected. Here's a simple example (without the use of bx:select2PickerCombo):
<xp:listBox id="listBoxProperties" value="#{viewScope.selectedProperty}" style="width:250px">
<xp:selectItems>
<xp:this.value><![CDATA[#{javascript:["1","2","3"]}]]></xp:this.value>
</xp:selectItems>
<xp:eventHandler event="onchange" submit="true" refreshMode="partial" refreshId="refreshField"></xp:eventHandler>
</xp:listBox>
<!--<bx:select2PickerCombo id="select2Property" for="listBoxProperties" placeHolder="-Select a Property-" binding="#{javascript:viewScope.selectedProperty}"></bx:select2PickerCombo>-->
<xp:text escape="true" id="refreshField" value="#{javascript:viewScope.selectedProperty;}"></xp:text>
You need to manually add Select2 to your listBox to get the onchange event to work. See this answer for more details: Bootstrap4XPages plugin: how to catch a change event with Select2 Picker?

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.

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

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>

Resources