Cannot get Repeat control to break out multi-value field - xpages

What I am trying to do should be very simple. I have a multi-value text field called activityLog. The field is set via a java method when various things happen. If I look at the backend data, it clearly is a multivalue field.
I went through the steps in Dave Leedy's Notes-in-9 #14, as well as tried to follow Tommy Valand's blog post here --> http://dontpanic82.blogspot.com/2011/03/repeat-controls-and-multivalue-fields.html. Neither seems to work for me. The result is that they list contents that are concatenated together in both cases.
The values ARE returned as a VECTOR. I can verify this by getting the size(), and by isolating specific elements using elementAt(). I was about to just make a for loop to get around this, but I want to know why it is failing. Repeats always seem to give me trouble. I was also want to leave straight forward code for me or future developers. There is probably something easy that I am missing.
<xp:repeat id="repeat1" var="rowData">
<xp:this.value><![CDATA[#{javascript:document1.getItemValue("activityLog")}]]></xp:this.value>
<xp:text escape="true" id="computedField14" value="#{javascript:rowData}">
</xp:text>
</xp:repeat>
UPDATE: The Repeat control is reading the Vector, but is NOT adding a new line automatically after each iteration. Trying to add a new line manually is not working, having tried JS "/n" & "/r" and #NewLine()

The use of <br/> or <xp:br /> suggested by Tim is an effective approach. But consider fixing the issue in the <xp:text/ > element instead.
The <xp:text />element creates a <span /> in the output HTML by default. Spans are, by default, displayed inline meaning that they do not have a line feed ahead of them. But you can add a CSS rule to the element which makes it a block element instead of inline. Add this CSS using a style tag: <xp:text style="display:block;" />or in a CSS class that include the rule {display: block}
A final way is to tell the the page to render a <div /> tag instead of a <span /> tag. DIVs are, by default, block elements. Do this with: <xp:text tagName="div"/>. (however, be careful because the default behavior of both DIVs and SPANs can be changed).

Browsers ignore whitespace except inside tags specifically intended to display whitespace. Add a break tag (<br />) or component (<xp:br />) inside the repeat, and the browser will display a new line for each iteration of the repeat.

There is a simple way of breaking out a multivalue fields, you only missed one thing. Accessing each row, I've added the rowColl variable indexVar="rowColl" and so you access it rowData[rowColl]
<xp:repeat id="repeat1" var="rowData" indexVar="rowColl">
<xp:this.value><![CDATA[#{javascript:document1.getItemValue("activityLog")}]]></xp:this.value>
<xp:text escape="true" id="computedField14" value="#{javascript:rowData[rowColl]}">
</xp:text>
</xp:repeat>

Related

Issue with pagers while Repeating Repeaters

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.

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 binding within a repeat control

In a purchase order module, we need to ask certain questions depending on the source selection method, competition type and total cost of the PO. These questions are likely to change over time and in between different instances of the database.
So I have a view containing the questions, so that I can add questions dynamically to my XPage without needing to change the code. The answer to each question will be stored in a field. So, the document that contains the question has a field called FieldName that is used to supply the field name that will be used. Unfortunately, I am having no luck binding these dynamic fields to the document.
<xp:this.data>
<xp:dominoView var="competitionQuestionView"
viewName="CompetitionQuestions">
</xp:dominoView>
</xp:this.data>
<xp:repeat id="repeat2" rows="30" var="rowData" style="width:700px"
value="#{competitionQuestionView}">
<xp:label id="label1">
<xp:this.value><![CDATA[#{javascript:rowData.getColumnValue("Question");}]]></xp:this.value>
</xp:label>
<xp:inputText id="inputText1">
<xp:this.rendered><![CDATA[#{javascript:rowData.getColumnValue("FieldType") == "Text Box"; }]]></xp:this.rendered>
<xp:this.value><![CDATA[#{javascript:poDoc[rowData.getColumnValue ("FieldName")];}]]></xp:this.value>
</xp:inputText>
</xp:repeat>
I've tried various ways to do this, including making a dynamicInputText custom control to pass in the field name, but without luck. The closest I got was when I used this:
<xp:this.value>
<![CDATA[#{javascript:tmp = rowData.getColumnValue ("FieldName");'#{poDoc.'+tmp+'}';}]]>
</xp:this.value>
That gave me something like #{poDoc.justification}, which was what I wanted to pass to the 'binding', but it ended up displaying as the text value.
I did try using $ to compute the value on load, but I am guessing that it didn't work because my (and the rowData) view is not available on load. That would eventually present a problem when I wanted to use partial refreshes due to updates on the criteria for which fields I want to display anyway.
Some of the answers to other questions looked promising, but no code was provided, so I couldn't figure it out.
Behind the scenes, all data sources use the methods getValue and setValue to (respectively) read and write data. In the case of a Domino document data source, the expression #{currentDocument.fieldName} gets translated at runtime to either currentDocument.getValue('fieldName') or currentDocument.setValue('fieldName', postedValue), depending on whether the current operation is a read or a write.
If you set the value attribute of an otherwise editable component to a SSJS value binding, then it can't do this auto-translation... so it just treats every operation as a read.
In other words, for read/write to work, it has to be a "prefixless" expression.
There are several ways to handle this, but the easiest is to use a data context to map a SSJS expression to a single variable. Data contexts can be attached to the view root or to a panel, so in your example, you'd want to wrap your repeat contents in a panel:
<xp:repeat id="repeat2" rows="30" var="rowData" style="width:700px"
value="#{competitionQuestionView}">
<xp:panel>
<xp:this.dataContexts>
<xp:dataContext var="fieldName">
<xp:this.value><![CDATA[#{javascript:rowData.getColumnValue ("FieldName");}]]></xp:this.value>
</xp:dataContext>
</xp:this.dataContexts>
<xp:label id="label1">
<xp:this.value><![CDATA[#{javascript:rowData.getColumnValue("Question");}]]> </xp:this.value>
</xp:label>
<xp:inputText id="inputText1" value="#{poDoc[fieldName]}">
<xp:this.rendered><![CDATA[#{javascript:rowData.getColumnValue("FieldType") == "Text Box"; }]]></xp:this.rendered>
</xp:inputText>
</xp:panel>
</xp:repeat>
So for each member of the repeat, the variable fieldName becomes the column value for that row. Then in the value attribute of the input component, the array syntax is used (instead of the usual dot syntax) since we want to use a variable to specify the field name instead of hardcoding the name.
In theory, however, you should be able to skip the data context entirely, and just set the following to be the value expression for the field:
#{poDoc[rowData.FieldName]}
In the context of the default ("prefixless") EL resolver, rowData.FieldName should return precisely the same value that rowData.getColumnValue("FieldName") returns in the context of a SSJS expression.
Finally, I would recommend reading this Expression Language tutorial to become familiar with all of the things that you can do in core EL without resorting to SSJS.

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