Issue with Dynamic View Panel and onColumnClick (CSJS) event - xpages

Using the Dynamic View Panel and trying in the onColumnClick event with client side JS to get the UNID or NoteID of the row the user clicked on. However, the XPage does not load with the error shown below
Error while executing JavaScript action expression
Script interpreter error, line=1, col=9: [ReferenceError] 'rowData' not found
JavaScript code
1: rowData.getNoteID()
The complete XPage code is shown below. Is there a way to get a handle on the row information to build this event handler? Goal is to use client side JS to open a new tab to show the document the user clicked on.
Howard
<xe:dynamicViewPanel
id="dynamicViewPanel1"
var="rowData"
indexVar="index">
<xe:this.data>
<xp:dominoView
var="view1"
viewName="testview">
</xp:dominoView>
</xe:this.data>
<xp:eventHandler
event="onColumnClick"
submit="false">
<xe:this.script><![CDATA[alert('#{javascript:rowData.getNoteID()}');]]></xe:this.script>
</xp:eventHandler></xe:dynamicViewPanel></xp:view>

You can't calculate row's noteId with alert('#{javascript:rowData.getNoteID()}') in CSJS code as this is executed on server side before all the rows get rendered.
Use rowAttrs property instead. It allows you to add an attribute to rendered table row. You'd add an attribute "noteId":
<xe:this.rowAttrs>
<xp:attr
name="noteId"
value="#{javascript:rowData.getNoteID()}">
</xp:attr>
</xe:this.rowAttrs>
The rendered html looks like this then:
The onColumnClick event is fired on rendered
<a id="view:_id1:dynamicViewPanel1:0:_id3:_internalColumnLink" href="#" ...
You can get this element/node in onColumnClick's CSJS code with thisEvent.target.
From there you "walk" upwards with .parentNode twice and get the element <tr ..> with attribute "noteId".
Finally, read the attribute with .getAttribute("noteId").
This is your example with all changes:
<xe:dynamicViewPanel
id="dynamicViewPanel1"
var="rowData"
indexVar="index">
<xe:this.data>
<xp:dominoView
var="view1"
viewName="testview">
</xp:dominoView>
</xe:this.data>
<xe:this.rowAttrs>
<xp:attr
name="noteId"
value="#{javascript:rowData.getNoteID()}">
</xp:attr>
</xe:this.rowAttrs>
<xp:eventHandler
event="onColumnClick"
submit="false">
<xe:this.script><![CDATA[
alert(thisEvent.target.parentNode.parentNode.getAttribute("noteId"));
]]></xe:this.script>
</xp:eventHandler>
</xe:dynamicViewPanel>

Related

Data handling with nested repeat controls

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>
...

Save Document via XPage DataSource and computeWithForm="onsave"

when i save a Notes Document in a XPage with computeWithForm="onsave" there is nothing computed at the form.
I thought that the computeWithForm Option trigger the "QuerySave" Event in the form, but am i wrong ?
I know that i could use the querySaveDocument Event at the Xpage DataSource, but there is a lot of lotusscript logic :-)
form:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE form SYSTEM 'xmlschemas/domino_9_0_1.dtd'>
<form name='testform' xmlns='http://www.lotus.com/dxl' version='9.0' maintenanceversion='1.9'
replicaid='C125827800263D1B' noquery='true' publicaccess='false' designerversion='8.5.3'
recalc='true' renderpassthrough='true'>
<code event='querysave'><lotusscript
>Sub Querysave(Source As Notesuidocument, Continue As Variant)
Dim doc As NotesDocument
Set doc = Source.Document
doc.test = "123"
Call doc.Save( True, False )
End Sub</lotusscript></code>
<body><richtext>
<pardef id='1' leftmargin='1in' hide='notes web' tabs='L6.9375in'/>
<par def='1'/>
<pardef id='2'/>
<par def='2'><run><font name='Verdana' pitch='variable' truetype='true' familyid='20'/><field
usenotesstyle='false' height='0.2500in' width='4.7243in' multiline='true'
borderstyle='single' type='text' kind='editable' name='title'/></run></par>
<par def='2'><field type='text' kind='editable' name='test'/><compositedata
type='98' prevtype='65418' nexttype='222' afterparcount='6' containertype='65418'
aftercontainercount='1' afterbegincount='3'>
Yg4BAIQAAAAAAAAAAAA=
</compositedata></par></richtext></body>
<item name='$$ScriptName' summary='false' sign='true'><text>testform</text></item></form>
Xpage:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:this.data>
<xp:dominoDocument var="document1" formName="testform" computeWithForm="onsave" />
</xp:this.data>
<xp:inputText value="#{document1.titel}" id="titel1" />
<xp:br></xp:br>
<xp:button value="save" id="button1">
<xp:eventHandler event="onclick" submit="true" refreshMode="complete">
<xp:this.action>
<xp:saveDocument var="document1" />
</xp:this.action>
</xp:eventHandler>
</xp:button>
</xp:view>
computeWithForm only runs Input Translation and Input Validation formulas.
If you want to retain the LS logic, one option would be to move it to an agent (I believe you would need to change UIDocument classes to Document classes and re-code accordingly) and call the agent from XPages. It's not something I've done though, so I can't point you towards documentation.
That's a misunderstanding! compute with form executes the #Formula that are in the associated form. It does not execute any form events or run LotusScript. If you want to use LotusScript, you need to pull it into an agent and explicitly call that agent (hint: Don't bother, translate your script to SSJS or Java instead).
That aside: you shouldn't call doc.save in a querySave event - there will be a save action anyway. You hit the disk twice (that's for Notes client apps, as mentioned that code doesn't run in XPages)

XSP JSF FacesContext addMessage does not always work

I have a JSF page where the user is entering several information. When the user submits the data it is first validated and if the submission is succesful the user should get a FacesMessage also.
I have a custom control for a menubar which I am using in every single page:
<xp:view xmlns:xp="http://www.ibm.com/xsp/core" xmlns:xc="http://www.ibm.com/xsp/custom">
<xp:panel>
<xp:this.facets>
<!-- Some Logos and stuff -->
<xp:panel xp:key="contentFacet">
<xp:callback facetName="contentContainer"></xp:callback>
</xp:panel>
</xp:this.facets>
</xp:panel>
</xp:view>
The menu is then used the following way:
<xc:cc_layout_main><xp:this.facets>
<xp:panel xp:key="contentContainer">
<xc:cc_content_form></xc:cc_content_form></xp:panel>
</xp:this.facets></xc:cc_layout_main>
The cc_content_form uses this control:
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:messages id="messages1" styleClass="test" layout="table"
errorClass="alert-warning" fatalClass="alert-danger"
infoClass="alert-info" warnClass="alert-warning" showDetail="true"
showSummary="true" disableTheme="false" globalOnly="false">
</xp:messages>
<xp:scriptBlock id="scriptBlock1">
<xp:this.value><![CDATA[
// Validierungs Nachrichten entfernen
var delayMs = 750;
$('body')
.on('click', function(event){
x$('#{id:messages1}').delay(delayMs).fadeOut(500);
})
.on('keyup', function(event){
x$('#{id:messages1}').delay(delayMs).fadeOut(500);
})
]]></xp:this.value>
</xp:scriptBlock>
</xp:view>
In the submit function I am using this in the validation function:
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "error", "some error"));
This call is wrapped in a helper class method with a singletone pattern so it can be used from all pages. For the validation messages this works just fine but further down in the valdiation method before I return the actionResult "xsp-success" it does not work. No message shows up, I do not get any Exception.
Can anybody help on this?
Turns out I messed up the NavigationRules ... Its working just fine

Xpages: Hide/Show div based on radio button values

I am binding all of my components to a java source. One field is a radio button, and if the user selects "Other" as a value, then I must show an field labeled "Other" for the user to fill in.
I cannot use SJSS to get the value of the radio button as it doesn't exist yet. But then how do I found out what the user selected? In the change event should I set a viewScope var and refresh the secondary area and check for the value of the viewScope var?
<xc:cc_CommonFormField
id="cc_CommonFormField14"
label="Savings"
placeholder="">
<xp:this.facets>
<xp:radioGroup
xp:key="field"
id="radioGroup1"
value="#{doc.prjSav}">
<xp:selectItem
itemLabel="Financial"
itemValue="Financial">
</xp:selectItem>
<xp:selectItem
itemLabel="Safety"
itemValue="Safety">
</xp:selectItem>
<xp:eventHandler
event="onchange"
submit="true"
refreshMode="partial" refreshId="refresh1">
</xp:eventHandler>
</xp:radioGroup>
</xp:this.facets>
<div id = "refresh1">
<div id = "innerRefresh"
rendered="#{javascript:what to put here}">
</xc:cc_CommonFormField>
<xc:cc_CommonFormField placeholder="Enter Other Reason...">
<xp:this.facets>
<xp:inputText
id="inputText3"
xp:key="field"
value="#{doc.prjOther}">
</xp:inputText>
</xp:this.facets>
</xc:cc_CommonFormField>
</div>
</div>
Here is the code that is correct:
<xc:cc_CommonFormField
id="cc_CommonFormField14"
label="Savings and/or Revenue From the Project"
placeholder="">
<xp:this.facets>
<xp:radioGroup
xp:key="field"
id="radioGroup1"
value="#{doc.prjSav}">
<xp:selectItem
itemLabel="Financial"
itemValue="Financial">
</xp:selectItem>
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="norefresh">
<xp:this.script><![CDATA[var result=null;
for(var i=0; i<document.forms[0].elements.length; i++){
if(document.forms[0].elements[i].name=="#{id:radioGroup1}" ){
if(document.forms[0].elements[i].checked == true){
result=document.forms[0].elements[i].value;
break; //remove this if for check box groups and collect multiple values above instead
}}}
var sel = x$("#{id:innerRefresh}");
if (result == "Other")
{
x$("#{id:innerRefresh}").removeClass("scoHidden");
x$("#{id:innerRefresh}").addClass("scoVisible");
}
else
{
x$("#{id:innerRefresh}").removeClass("scoVisible")
x$("#{id:innerRefresh}").addClass("scoHidden");
}
XSP.partialRefreshGet("#{id:innerRefresh}")]]></xp:this.script>
</xp:eventHandler></xp:radioGroup>
</xp:this.facets>
</xc:cc_CommonFormField>
<div id="refresh1">
<xp:div id="innerRefresh" styleClass="scoHidden">
<xc:cc_CommonFormField placeholder="Enter Other Reason...">
<xp:this.facets>
<xp:inputText
id="inputText3"
xp:key="field"
value="#{doc.prjOther}">
</xp:inputText>
</xp:this.facets>
</xc:cc_CommonFormField>
There are 2 approaches to solve this:
- server side rendering using SSJS
- client side rendering using JS
When you opt for the former, you have to submit your data (partial including the hidden div) to recompute your rendered property.
Client side: you render the div in any case, but give it a class attribute that maps to CSS display:none.
In your client side JS you add an onChange handler to the radio buttons and change the class to something visible (and back if others are unselected). This saves you from a server round trip.
Only caveat: (best handled in your Java) you need to dismiss an eventually entered value in the field for other if other wasn't selected.

How do I refresh a repeat that is updated using a dialog

I'm having a problem getting a repeat to refresh when the underlying value is changed using a dialog.
This is the div that contains the repeat:
<xp:div style="display:none;">
<xp:inputText id="linkages" value="#{procureDoc.Linkages}" multipleTrim="true" style="color:cornflowerblue;" multipleSeparator=";">
</xp:inputText>
</xp:div>
<xp:label value="Linkages:" id="linkageLabel" style="font-weight:bold;"></xp:label>
<xp:div id="linkageDiv">
<ul>
<xp:repeat id="linkagesDisplayRepeat" rows="30" var="rowData" indexVar="index" value="#{procureDoc.Linkages}">
<li>
<xp:text escape="true" id="computedField7">
<xp:this.value><![CDATA[#{javascript:rowData;}]]></xp:this.value>
</xp:text>
</li>
</xp:repeat>
</ul>
</xp:div>
Here's the save button from the dialog. It does happen to sit in another custom control, but I don't think that's the problem.
<xp:button value="Save" id="saveButton">
<xp:eventHandler event="onclick" submit="true" refreshMode="complete">
<xp:this.script><![CDATA[var linkageSelection = document.getElementById("#{id:linkageComboBox}").value;
var linkageUpload = "";
if (linkageSelection == "Use Category") {
linkageUpload = document.getElementById("#{id:linkageCategoryComboBox}").value;
}
else { linkageUpload = linkageSelection;}
var currentLinkages = document.getElementById("#{id:linkages}").value;
if ( currentLinkages == "" ) {
document.getElementById("#{id:linkages}").value = linkageUpload;
} else {
document.getElementById("#{id:linkages}").value = document.getElementById("#{id:linkages}").value + ";" + linkageUpload;
}
XSP.closeDialog('#{id:linkageDialog}');]]></xp:this.script>
</xp:eventHandler>
</xp:button>
I can put a button on the XPage that just refreshes and it will refresh my repeat with the value selected in the dialog, but just using my save button doesn't do it.
I'm sure I'm missing something simple, but I just can't see it.
You could use the onComplete event from your SSJS eventhandler to call a CSJS script like
XSP.partialRefreshGet("#{id:linkagesDisplayRepeat}");
to refresh the repeat control.
There's a second parameter to the client-side closeDialog() method, denoting the ID of the component to partially refresh. That basically adds the onComplete for you.
SSJS corresponding method uses the ID of the component to subsequently refresh as the only parameter (obviously the method is called on the component being closed, which is the first parameter in CSJS).

Resources