Issue with pagers while Repeating Repeaters - xpages

I am using the current newest ExtLibs, and a Domino 9.0.1 Server with FP3.
I am trying set up my own multi level categorized view with repeat controls that are getting data from an SQLite Database, and I am hitting a snag with the pager controls.
Structure
I have a primary Repeat Control that displays 5 categories at a time (Sections). This is connected to the primary pager.
Inside this repeat control, I have a panel that has another repeat control for the actual data. (In some cases I put another section to create multiple levels with another repeater...)
* The problem
When I have multiple pages on the primary repeater and I start out at page one, then in one embedded pager, I select page 2, then I select a different page on the primary pager, then all embedded pagers are automatically reset to page 2. It seems to me as though the request scope variable that controls the embedded pager number is shared among all repeated instances.
What is the best way to go about solving this problem? And am I barking up the wrong tree by repeating repeaters to begin with?
******************************************* Stand alone XPage for reproduction
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:pager layout="Previous Group Next" partialRefresh="true"
id="pager1" for="repeat1" panelPosition="left">
</xp:pager>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:repeat id="repeat1" rows="5" var="primaryList">
<xp:this.value><![CDATA[#{javascript:var list:java.util.ArrayList = new java.util.ArrayList();
list.add("1")
list.add("2")
list.add("3")
list.add("4")
list.add("5")
list.add("6")
list.add("7")
list.add("8")
list.add("9")
list.add("10")
list.add("11")
list.add("12")
list.add("13")
list.add("14")
list.add("15")
list.add("16")
list.add("17")
list.add("18")
return list;}]]></xp:this.value>
<xp:section id="section1" header="#{javascript:primaryList}">
<xp:br></xp:br>
<xp:panel style="padding:0px 0px 5px 40px">
<xp:pager layout="Previous Group Next"
partialRefresh="true" id="pager2" for="repeat2"
panelPosition="left">
</xp:pager>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:repeat id="repeat2" rows="5" var="innerRepeat">
<xp:this.value><![CDATA[#{javascript:var list:java.util.ArrayList = new java.util.ArrayList();
list.add("1")
list.add("2")
list.add("3")
list.add("4")
list.add("5")
list.add("6")
list.add("7")
list.add("8")
list.add("9")
list.add("10")
list.add("11")
list.add("12")
list.add("13")
list.add("14")
list.add("15")
list.add("16")
list.add("17")
list.add("18")
return list;}]]></xp:this.value>
<xp:inputText id="inputText1"
value="#{javascript:innerRepeat.toString();}">
</xp:inputText>
<xp:br></xp:br>
</xp:repeat>
</xp:panel></xp:section>
</xp:repeat>
</xp:view>
steps to reproduce
be on page one on primary pager.
in any sub pager, select page 2.
Notice correctness of other pagers
Switch main pager to page 3
Notice all sub pagers on on page 2

I don't think it's a bug. It's working as designed (!)...
The problem is that the repeat does not create multiple pagers inside for each iteration. So there is basically only one pager and that pager keeps only one PagerState. That's why they are acting weird.
When you use repeatControls="true" option, repeat will not iterate only one component, instead it will really create multiple components for each iteration. However, in that case, you can't use Pager component for such a repeat, because the new repeat component does not have an iterator anymore. It will not be able to find 'pagination' and will throw 'division of zero' error.
Here, I have explained the difference between two modes of Repeat component (from slide #36):
https://speakerdeck.com/sbasegmez/engage-2015-10-mistakes-you-and-every-xpages-developer-make-yes-i-said-you?slide=36
The solution would be creating your own pager design for inner repeats.
Update:
Another approach is marking the outer Repeat as repeatControls="true" and removing the outer pager. In such a configuration you'll have multiple pager components in the component tree but you have to sacrifice the outer pager. In this configuration, you'll need to load all elements in the outer repeat (because they can't be actively manipulated anymore).
To mimic the pager behaviour you can design a custom pager and use rendered attribute for inner repeats. It will slow the page down, but will provide 'paging' feeling to the user.

Related

Can I filter an Xpages search bar

I have been given an XPages project which I did not develop. The project has a OneUILayout that includes a Search Bar "facet". Is it possible to code a filter into the search bar facet so that retrieved records are omitted that have a field with a certain value. I have very little experience with XPages. The search results are output to a OneUI_searchpage.xsp where an edit box displays the search string then a dynamic View Panel shown the retrieved records. I have attached the source code for these two items below. Thank you
<xp:label value="Search String:" id="label1"></xp:label>
<xp:inputText id="inputText1" value="#{param.search}"></xp:inputText>
<xp:panel id="maincontentpanel">
<xe:dynamicViewPanel rows="30" id="dynamicViewPanel1"
width="100%">
<xe:this.data>
<xp:dominoView viewName="ContractsFlatByYear"
var="view">
<xp:this.search><![CDATA[#{javascript:return
param.search;}]]></xp:this.search>
</xp:dominoView>
</xe:this.data>
</xe:dynamicViewPanel>
After some consultation with stwissel below, I amended the application to have a check box on the search results xpage with it checked by default and created an additional view for the same output. One view to show cancelled contracts and one to omit cancelled contracts. The relevant Xpage section now looks like as follows;
<xp:checkBox text="Omit Cancelled Contracts"
id="OmitCancelled" defaultChecked="true" checkedValue="True"
uncheckedValue="False" style="padding-left:5.0em" value="#
{viewScope.viewSel}">
<xp:eventHandler event="onchange" submit="true" refreshMode="partial"
refreshId="dynamicViewPanel1"></xp:eventHandler>
</xp:checkBox>
<xp:panel id="maincontentpanel">
<xe:dynamicViewPanel rows="30" id="dynamicViewPanel1"
width="100%" partialRefresh="true">
<xe:this.data>
<xp:dominoView var="view">
<xp:this.viewName>
<![CDATA[#{javascript:var cancelledYesNo = viewScope.viewSel
= getComponent("OmitCancelled").getValue();
if(cancelledYesNo == "True"){
viewName = "ContractsFlatByYear"}
else {
viewName = "ContractsFlatByYearandCancelled"}}]]>
</xp:this.viewName>
<xp:this.search><![CDATA[#{javascript:return param.search;}]]
></xp:this.search>
</xp:dominoView>
</xe:this.data>
</xe:dynamicViewPanel>
This appears to work but I have the check box onChange event to apply a partial refresh on the dynamicviewpanel but only refreshes when I click on the dynamicviewpanel itself
The search bar facet only captures what you want to search and sends it to the specified XPages for processing.
You have 2 options:
Alter the facet to send the additional condition to the search page
Alter the search function in the search page (the one the query gets posted to) to filter that (if it is static).
Be aware: filtering in code is not a security feature (in case you were intending that). There's reader and author fields for that.
XPages in its core is JSF with some specialities around Domino. You might want to check out my article series on them.
Update
Based on the code snippet, your can get the desired result quite fast. Edit the view selection formula and add & conContractStatus <> "cancelled". You need to check first if that view is used elsewhere to show canceled contracts too. If that is the case, copy the view (eg add Active behind the name) and make the changes there.
Update 2
Your code doesn't return a value and you don't need to get to the component
<xp:this.viewName>
<![CDATA[#{javascript:return (viewScope.viewSel=="True") ? "ContractsFlatByYear" : "ContractsFlatByYearandCancelled";}]]>
</xp:this.viewName>
Let us know how it goes

Replace embedded views with multiple data sources

I am building an application that will have a parent documents with up to 5 different child documents attached to the parent. Each parent can have one and only 1 of each of the 5 types of child documents.
In traditional notes programming I would probably put an embedded view within the parent documents form. I was starting down that path when I wondered if this wouldn't be better done in Xpages with one Xpage that has multiple datasources.
A complicating factor is that each form/data source will have a different group of people who own it - only they can create or edit it. But I don't think this would be a problem.
Couple of ideas to consider ...
don't use response docs ... tie the related docs together via a document key like unid
put the create/edit function for the five child docs in their own separate panels ... childpanel1, childpanel2, etc.
add the datasources for the child docs in the respective panels ... childdoc1, childdoc2, etc
restrict edit access via the acl property for each panel
with multiple data sources on an XPage remember to set ignoreRequestParams="true" for the childdoc data sources
For example, here's the panel structure for the first child doc:
<xp:panel id="childpanel1">
<xp:this.data>
<xp:dominoDocument var="childdoc1" formName="ChildForm1"
ignoreRequestParams="true">
</xp:dominoDocument>
</xp:this.data>
<xp:this.acl>
<xp:acl>
<xp:this.entries>
<xp:aclEntry type="GROUP" name="doc1group" right="EDITOR">
</xp:aclEntry>
</xp:this.entries>
</xp:acl>
</xp:this.acl>
</xp:panel>
More on ignoreRequestParams:
In a two-page application involving a "view" XPage and a "document" XPage, when the application user clicks a link on the view page to open the document on the document page the ID for the document to open is passed with the REQUEST parameters. You can see this in the resulting URL for the document page which will have the ID for the document to open appended to the URL address, for example:
&documentId=49530CA58D17CCE5852575150069D857&action=openDocument
This works perfectly when the document page has only one Domino document data source. However, in the embedded view application the "document" XPage will also include a data source for the xp:viewPanel. When ignoreRequestParams is NOT true for the Domino view data source (and for any other additional data sources on the page, like childdoc1) then the parameters passed in the request ARE evaluated to determine which view entries to display in the xp:viewPasnel. These request parameters point to a UNID that is not the desired data source for the view so no view entries are displayed.
Note, you may not need to set ignoreRequestParams to true for ALL of the data sources on the XPage ... just be aware of this setting and what it does when things get wonky for one of the view or document data sources on the page.
Not sure if your question is how to limit the form creation or how to have something like an embedded view in xPages. Both are easier to do in xPages.
Basically you just need to make the children responses of the parent. I prefer to not use normal response documents and prefer instead to add a field called uid with the unid of the parent to the children. I then filter my datasource to match the uid of the parent document. You can use view controls for this but repeats look even better.
Its a great place to start using dialogs as you can have the second data source in the dialog and the user never needs to leave the page.
As far as restricting the view creation you can do something like hide the button to create a new child based on a criteria.
This action will add the unid of document1 to document3
<xp:modifyField name="uid"
value="#{javascript:document1.getDocument().getUniversalID()}" var="document3">
</xp:modifyField>
Here is the code I use for a repeat control datasource. The view is categorized by uid field
<xp:dominoView var="view7" viewName="VIEWNAME"
keys="#{javascript:document1.getDocument().getUniversalID()}">
</xp:dominoView>
Then the repeat control just grabs the fields you want.
<xp:table styleClass="table">
<xp:tr>
<xp:td>Column Header</xp:td>
<xp:td>Column Header</xp:td>
<xp:td>Column Header</xp:td>
</xp:tr>
<xp:repeat id="repeat3" rows="30" value="#{view7}"
var="rowData">
<xp:tr>
<xp:td>
<xp:text escape="true"
id="computedField3" value="#{rowData.columnname1}">
</xp:text>
</xp:td>
<xp:td>
<xp:text escape="true"
id="computedField4" value="#{rowData.columnname2}">
</xp:text>
</xp:td>
<xp:td>
<xp:text escape="true"
id="computedField5" value="#{rowData.columnname3}">
</xp:text>
</xp:td>
</xp:tr>
</xp:repeat>
</xp:table>
Ignore some of the styleclasses as I am using bootstrap.

Is it possible to generate viewColumn using repeat ? It doesnt work for me :-(

Is it possible to generate viewColumn dynamicly using repeat control,? I have a viewPanel and repeater that runs over all columns in this view and try to create viewColumn control for each as below. It doesnt throw any error for me but also no table apear on screen ... I would like to generate it dynamicly as I have many existing views with up to 20 columns so maintaining this manualy would be not so nice. I also need to use viewPanel because a first view column is categorized so I need the viewPanel mechanism for epanding/collapsing these categories.
<xp:viewPanel rows="30" id="viewPanelMain" var="row" value="#{viewDS}">
<xp:repeat id="repeat1" rows="100" value="#{javascript:myView.getColumns()}" disableOutputTag="true" var="column">
<xp:viewColumn>
<xp:this.columnName><![CDATA[#{javascript:column.getItemName()}]]></xp:this.columnName>
<xp:viewColumnHeader value="#{javascript:column.getTitle()}"></xp:viewColumnHeader>
</xp:viewColumn>
</xp:repeat>
</xp:viewPanel>
Mabe there is some better way how to achieve the same result ... Any idea?
Have a look at the Dynamic View Panel control from the Extension Library (included as part of the Domino 9 installation). The following should work using your example:
<xe:dynamicViewPanel value="#{viewDS}" id="dynamicViewPanel1" var="viewEntry">
</xe:dynamicViewPanel>
You can then consider customizing the look and fel using a customizer bean, you can add a pager, you can add an onColumnClick event etc.
My repeat works, inside a viewPanel
...to create numerous view columns.
Need to set "true" -- for repeatControls and removeRepeat

Set readonly property inside a nested panel

If I have an outer panel in which the readonly property is true, is there any way to created inner panels in which the content can be editable?
The use case is a mobile page with a number of fields plus multiple RoundedRectLists. I would like to add a search control to each RoundedRectList to filter the content of those lists. I do not want the fields to always be editable. I need the search control to be editable so I can enter a search value without toggling the entire form. At the moment I have readonly=false set for the inner panel but the search control only becomes editable when the readonly for the outer panel is also set to false.
I know I can created separate panel that are not nested, but this design pattern of nested panels is quite common and I am sure there is n XPages guru out there that has solved this...
I am not the XPage guru you are searching for but i have a workaround, =)
As far as i know if you set the outer panel to readonly="true" all your inner inputText boxes will output no <inputField> in your html just a <span>. Thats also very anoying if you want to have a field that looks like a input but is just disabled for input. That is achieved if you set the to disabled="true" and showReadonlyAsDisabled="true".
I would recomend setting all readonly="false" and use dojo.query to set the disabled propertie on page load like:
<xp:scriptBlock>
<xp:this.value><![CDATA[dojo.ready(function(){
dojo.query(".input").forEach(function(node, index, array){
node.disabled = true;
}
)
});]]></xp:this.value>
</xp:scriptBlock>
<xp:panel>
<xp:inputText id="inputText1" value="#{viewScope.in1}" styleClass="input"
defaultValue="Txt0" >
</xp:inputText>
<xp:inputText id="inputText2" value="#{viewScope.in2}" styleClass="input"
defaultValue="Txt1">
</xp:inputText>
<xp:inputText id="inputText3" value="#{viewScope.in3}" styleClass="input"
defaultValue="Txt2">
</xp:inputText>
</xp:panel>
Another benefit of this solution you can set them editable on clientSide without any Server calls.
Hope it helps.

Dynamic Table from Lotus Notes to XPages within a form

I am taking an existing Lotus Notes database and converting to Xpages. There is one of those tables containing 3 multi-value fields, with New Line as the seperator and the "Add New", "Modify" and "Delete" buttons controlling how the data is entered and removed. The customer would like the XPage to look as similar to the Notes GUI as possible, and I was thinking I would use the dijit dialog box to do the add new line and figure out the delete and modify. But from what I can tell, the dialog box can only be used on client-side and the data input into the dialog box can't be brought down onto the Xpage. Is this true? I was thinking I would use an editable field within a repeat, but I also couldn't that working properly.
Basically, it the solution has to show the multi-value fields for past documents and also be able to allow users to edit those older document...plus work similar/exactly the way as in the past when creating new docs.
Thanks in advance for any help I can get on this as it seems a ton easier than I am probably making it out to be.
I just wanted to update after the solution below, which appears to be an excellent way to solve this problem. However, as an admitted XPages novice, I am really struggling with the application of this concept. This is what I have, and it obviously isn't working.
Logically, this sounds like a great solution. However, I am no xpages expert and I simply can't get this working properly even to get started. Anything at all that would make this easier for me to even get started would be a big help. I'm not one to usually look for "the answer"...I'm just having difficulty getting a handle on this Multi-value field table issue. Thanks again in advance...here's what I wrote that is coming up with a 500 error. "A" is the multi-value field name.
<xp:table>
<xp:tr>
<xp:td>
<xp:repeat id="repeat1" rows="30" var="rowdata">
<xp:this.value><![CDATA[#{javascript:document1.getItemValue("A")}]]></xp:this.value>
<xp:tr id="valueRow">
<xp:td>
<xp:text
value="#{javascript: return rowdata[i]}" />
</xp:td>
<xp:eventHandler event="onclick" submit="false"
refreshMode="partial" execMode="partial" execId="valueRow"
immediate="true">
<xp:this.action>
<![CDATA[#{javascript:document1.getItemValue("A")}]]>
</xp:this.action>
</xp:eventHandler>
</xp:tr>
</xp:repeat>
</xp:td>
</xp:tr>
</xp:table>
I would say do the following
Create a repeat control which will extract the data from the multi value fields and print them read only. The repeat control will generate a tr structure with a event handler bound to it on the onclick event. something like this:
2 In the onclick event change the style of the tr clientside (using dojo) so people know they selected that row and set the id / identifier of that row in a scoped var
3 Above the repeat control add controls like add, remove, update. The add and update will open a dialog box and will read the data from the selected row ( or none if its a add action). The delete control will remove the data from the multiline value fields, save the document and refres the repeat control.
This should work.

Resources