I am using a dynamicViewPanel that displays a user's attachment information from a domino view. There are 4 columns which information is properly being displayed. When a user clicks the first column, another page opens with technical information about that attachment. What I need to happen is when a user clicks the 4th column, which displays [edit], a different page opens up which the user can edit the attachment information that is displaying. I am using UserViewCustomizer code.
Any suggestions on how to get the [edit] link to open a different page that the Attachment link?
My XPage Code:
<xe:dynamicViewPanel id="dynamicViewPanel2"
pageName="#{empty sessionScope.formName ? 'QuickResponseCode.xsp' : sessionScope.formName}"
var="viewEntry" customizerBean="com.cascorp.UserProfileViewCustomizer">
<xp:eventHandler event="onColumnClick" submit="true"
refreshMode="complete" id="Attachment">
<xp:this.action><![CDATA[# {javascript:sessionScope.hideAdd="Yes";
var srlNbr = viewEntry.getColumnValue("serialNbr");
var url="/qr/?"+srlNbr;
context.redirectToPage(url);}]]></xp:this.action>
</xp:eventHandler>
<xp:eventHandler event="onColumnClick" submit="true"refreshMode="complete" id="edit">
<xp:this.action><![CDATA[#{javascript: "I think something goes here" ]]></xp:this.action> </xp:eventHandler>
<xp:this.facets>
<xp:pager layout="Previous Group Next" partialRefresh="true" id="pager2" xp:key="footerPager"> </xp:pager>
<xe:pagerSaveState id="pagerSaveState1" xp:key="viewTitle"</xe:pagerSaveState>
<xp:pager layout="Previous Group Next" partialRefresh="true" id="pager1"
xp:key="headerPager"></xp:pager>
</xp:this.facets>
<xe:this.data>
<xp:dominoView var="view1"
databaseName="#{configBean.UserprofilesDbPath}" viewName="#{empty
sessionScope.viewName ? 'attachmentsbyowner' : sessionScope.viewName}"
keys="#
{javascript:session.getEffectiveUserName();}">
</xp:dominoView>
</xe:this.data>
</xe:dynamicViewPanel>
My ViewCustomize Code:
package com.cascorp;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import org.openntf.xsp.debugtoolbar.beans.DebugToolbarBean;
import com.ibm.xsp.extlib.component.dynamicview.UIDynamicViewPanel;
import com.ibm.xsp.extlib.builder.ControlBuilder.IControl;
import om.ibm.xsp.extlib.component.dynamicview.DominoDynamicColumnBuilder.DominoViewCustomizer;
import
com.ibm.xsp.extlib.component.dynamicview.UIDynamicViewPanel.DynamicColumn;
import com.ibm.xsp.extlib.component.dynamicview.ViewDesign.ColumnDef;
public class UserProfileViewCustomizer extends DominoViewCustomizer {
//this is used to customize the DynamicViewPanels used in the Order
Processing views
//#SuppressWarnings("unchecked")
#Override
public void afterCreateColumn(FacesContext context, int index,
ColumnDef colDef, IControl column) {
//Get a map of the session variables to read the view session scope variable
//Map svals = context.getExternalContext().getSessionMap();
//Create a variable for the current component
UIComponent columnComponent = column.getComponent();
//Create a reference to the column and set the links to open in read mode
DynamicColumn dynamicColumn = (DynamicColumn) columnComponent;
//To have every view open the selected documents in read mode add the following
dynamicColumn.setOpenDocAsReadonly(true);
DebugToolbarBean.get().info("name of column " + dynamicColumn.getColumnName());
/*
* If all you need to do is have the views open in read mode instead of
* edit mode then the above code is all you need.
* If you want to customize the view columns the the follow code can be
* used as an example.
*/
if (dynamicColumn.getColumnName().equalsIgnoreCase("Attachment")){
//make it a link
dynamicColumn.setDisplayAs("link");
DebugToolbarBean.get().info("make Attachment Column a link");
}
if (dynamicColumn.getColumnName().equalsIgnoreCase("edit")){
//make it a link
dynamicColumn.setDisplayAs("link");
DebugToolbarBean.get().info("make edit Column a link");
}
//Set column properties for specific views.
//if (svals.containsValue("processforkkit")) {
//Hide the first column in this view
if(dynamicColumn.getColumnName().equalsIgnoreCase("$2")){
dynamicColumn.setRendered(false);
}
if (colDef.isCategorized() ){
//set the expand and collapse images if the column is categorized
DebugToolbarBean.get().info("column is categorized: " + dynamicColumn.getColumnName());
// ExtendedColumnDef extColDef = (ExtendedColumnDef) colDef;
// DebugToolbarBean.get().info("twistie image: " + extColDef.twistieImage);
UIDynamicViewPanel.DynamicColumn col = (UIDynamicViewPanel.DynamicColumn)column.getComponent();
col.setExpandedImage("expand.png");
col.setCollapsedImage("collapse.png");
DebugToolbarBean.get().info("collapsed image: " + col.getCollapsedImage());
DebugToolbarBean.get().info("expanded image: " + col.getExpandedImage());
DebugToolbarBean.get().info("style class: " + col.getStyleClass());
col.setStyleClass("category_col");
}
super.afterCreateColumn(context, index, colDef, column);
}
}
I used a repeat control. See below:
<tbody>
<xp:repeat id="repeat1" var="rowData" indexVar="ind" value="#{view1}"
rows="10" repeatControls="true">
<tr>
<td>
<xp:text escape="false"> </xp:text>
</td>
<td>
<!--Create a link -->
<xp:link escape="true" text="#{rowData.Attachment}" id="link2"
value="#{configBean.HostURL}qr?#{rowData.serialNbr}">
</xp:link>
</td>
<td>
<xp:text escape="true" id="computedField6" value="#{rowData.serialNbr}">
</xp:text>
</td>
<td>
<xp:text escape="true" id="computedField2" value="#{rowData.modelNbr}">
</xp:text>
</td>
<td>
<!-- CREATE A 2ND LINK -->
<xp:link escape="true" id="link3" text="${langBean.labelEdit}">
<xp:this.value><![CDATA[#{javascript:importPackage(com.cascorp);
hostName = configBean.getValue("HostCommon")
dbName = configBean.getValue("UserprofilesDbPath")
try{ if(rowData.isDocument()){
return "MyProfileAttachment.xsp?databaseName="+hostName+"!!"+dbName+"&documentId="+rowData.getUniversalID()+"&action=editDocument"
}
} catch (e) {
return e.toString()
}}]]>
</xp:this.value>
</xp:link>
</td>
</tr>
</xp:repeat>
Related
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>
...
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).
I have a repeat control on an XPage displaying item summary. To view other details, the user clicks on a button (inside the repeat) which will set a viewScope variable to the rowIndex of the repeat and then open up a modal to display the remeaining item details.
I am unable to set the viewScope variable from the button. It's like it cannot get a handle to the rowIndex.
I have a computed text field set to display the rowIndex at the beginning of the repeat (which works fine).
I'm obviously missing something elementary. Any assistance would be appreciated.
Relevant source code follows:
<xp:repeat id="repeat1" rows="50" var="entry" indexVar="rowIndex">
<xp:this.value><![CDATA[#{javascript:var m = sessionScope.assetMap;
if(m!=null){
m.entrySet()
}}]]></xp:this.value>
<div>
<small>
<label>
<xp:text escape="true" id="num">
<xp:this.value><![CDATA[#{javascript:var itemNum:Number = parseInt(rowIndex + 1);
itemNum.toPrecision(0);}]]></xp:this.value>
</xp:text>
. Description:
</label>
<xp:text escape="true" id="desc">
<xp:this.value><![CDATA[#{javascript:#Word(entry.getValue(),"~",3);}]]> </xp:this.value>
</xp:text>
</small>
</div>
<br></br>
<div>
<xp:button type="button" value="Details ..." id="button2">
<xp:eventHandler event="onclick" submit="true" refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:try{var itemNum:Number = parseInt(rowIndex + 1);
viewScope.currentItem = itemNum.toPrecision(0);
}catch(e) {
requestScope.errstatus = e.toString();}}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
02/04/2015 Update:
I have isolated the problem to the rendered property of the custom control. The code works if I don't use the rendered property.
The code in the onCLientLoad of my XPage (CSJS) is:
$(window).on('load resize', function(){
var screenWidth = $(window).width();
var sDevice = '';
switch(true){
case (screenWidth < 768):
sDevice = 'mobile';
break;
case (screenWidth < 922):
sDevice = 'tablet';
break;
case (screenWidth >= 922):
sDevice = 'desktop'
}
XSP.partialRefreshPost( '#{id:pnlList}', {params: {'sDevice': sDevice}} );
});
And the source for my custom control in the XPage with the rendered property is:
<xp:panel rendered="#{javascript:param.sDevice == 'mobile'}">
<xc:ccSYS_AppLayoutReq_p xp:key="panelContentFacet">
<xp:this.facets>
<xc:cc_cartracking_p xp:key="contentFacet"></xc:cc_cartracking_p>
</xp:this.facets>
</xc:ccSYS_AppLayoutReq_p>
</xp:panel>
Thanks,
Dan
You have to set option "Set partial execution mode" execMode="partial" in your button.
Look for the whole answer at your colleague's question https://stackoverflow.com/a/28330481/2065611
i have a DataTable on my Xpage. I´ve filled with JavaScript an array with elements from 4 Notes Databases, which i´ve returned to the Table to show it on the DataTable.
This works, but now i need a search (edit box with search button) to search and update the results in the DataTable.
Is this possible? I only found a solution for a datatable, which is directly bind to a view, and nothing for a "manually" filled DataTable.
This is my Datatable code , which includes the JS code to create the array
<xp:dataTable id="dataTable3" rows="990" var="coll2Test"
style="width:70%" indexVar="idx">
<xp:this.facets>
<xp:pager partialRefresh="true"
layout="Previous Group Next"
xp:key="header" id="pager8">
</xp:pager>
</xp:this.facets>
<xp:this.value><![CDATA[#{javascript:
var fa:NotesDatabase = session.getDatabase(#DbName()[0], "???????");
var faSh:NotesDatabase = session.getDatabase(#DbName()[0],"???????");
var faKb:NotesDatabase = session.getDatabase(#DbName()[0],"???????");
var fa20:NotesDatabase = session.getDatabase(#DbName()[0],"???????");
var vFa:NotesView = fa.getView("???????");
var vSh:NotesView = faSh.getView("???????");
var vKb:NotesView = faKb.getView("????????");
var v20:NotesView = fa20.getView("???????");
var doc:NotesDocument = vFa.getFirstDocument();
var docSh:NotesDocument = vSh.getFirstDocument();
var docKb:NotesDocument = vKb.getFirstDocument();
var doc20:NotesDocument = v20.getFirstDocument();
var faArr= new Array();
while(doc!= null) {
var eintrag = new Array();
eintrag.push(doc.getItemValue("Name"));
eintrag.push(doc.getItemValue("Description"));
faArr.push(eintrag)
doc = vFa.getNextDocument(doc);
}
while(docSh!= null) {
var eintrag = new Array();
eintrag.push(docSh.getItemValue("Name"));
eintrag.push(docSh.getItemValue("Description"));
faArr.push(eintrag)
docSh = vSh.getNextDocument(docSh);
}
.....
//another 2 while
...
return faArr;}]]></xp:this.value><xp:table>
<xp:tr>
<xp:td>
<xp:label
value="Suche:" id="label14"
style="font- weight:bold">
</xp:label>
</xp:td>
<xp:td>
<xp:inputText
id="inputText7"
value="#
{viewScope.searchView6}">
</xp:inputText>
</xp:td>
<xp:td>
<xp:button value="suchen"
id="button7">
<xp:eventHandler
event="onclick"
submit="true" refreshMode="complete" immediate="false"
save="true" id="eventHandler7">
</xp:eventHandler>
</xp:button>
</xp:td>
</xp:tr>
</xp:table>
<xp:column id="column6">
<xp:this.facets>
<xp:label id="label11"
xp:key="header"
style="color:rgb(0,128,255);font-weight:bold"
value="Name">
</xp:label>
</xp:this.facets>
<xp:text escape="false"
id="computedField5">
<xp:this.value><![CDATA[#
{coll2Test[0]}]]></xp:this.value>
</xp:text>
</xp:column>
<xp:column id="column7">
<xp:this.facets>
<xp:label id="label12"
xp:key="header"
style="color:rgb(0,128,255);font-weight:bold"
value="Description">
</xp:label>
</xp:this.facets>
<xp:text escape="true" id="computedField6">
<xp:this.value><![CDATA[#
{coll2Test[1]}]]></xp:this.value>
</xp:text>
</xp:column>
</xp:dataTable>
Regards
Stefan
Write your completely filled JavaScript array into a view scope variable e.g. viewScope.AllEntries at the beginning. Fill your DataTable with
the complete array if no search string is entered.
the values matching search string if user entered a search string
This way you only need to read the data once from your four views and just filter it later depending on search string.
Move your code to beforePageLoad event. Add at the top
if (viewScope.AllEntries) {
return;
}
and replace the last line return faArr; with
viewScope.AllEntries = faArr;
This way you'll calculate faArr only once and store it in viewScope.AllEntries.
Replace your code in DataTable "value" with:
if (!viewScope.search) {
return viewScope.AllEntries;
}
...create and return an array (walking through viewScope.AllEntries)
...which contains only elements with search string viewScope.search
The search field can be an edit field bound to a viewScope like viewScope.search. Trigger a partial refresh of DataTable when user clicks on search button.
I am using Teamstudio Unplugged to try and Extend a table row with inputTexts bound to a document.
The function of this table is for the user to write down a observation and then a description of the observation.
After this the user should be able to press a button and get another row so he can add another observation.
I cannot partially refresh the entire table because it would cause the inputTexts to reload the value bound to them.
I cannot partially refresh the new row and change its styleclass with serverside scripts because IDs does not carry over from the designer.
Meaning that this in the designer:<xp:tr id="row1"> becomes this in the pc simulator: <tr>
So I can't solve this problem with a computed styleclass and a partial refresh. So i have now built a button included below but it only changes works twice. after that the onclick string never changes.
<xp:button value="Flere observationer" id="button4" styleClass="button">
<xp:this.attrs>
<xp:attr name="onclick">
<xp:this.value><![CDATA[#{javascript:
return "$('.row"+sessionScope.rows+"').removeClass('hidden');"
}]]></xp:this.value>
</xp:attr>
</xp:this.attrs>
<xp:eventHandler event="onclick" submit="true" refreshMode="partial"
refreshId="button4">
<xp:this.action>
<xp:executeScript script="#{javascript:sessionScope.rows++;}">
</xp:executeScript>
</xp:this.action>
</xp:eventHandler>
</xp:button>
This can be done in Unplugged using standard(-ish) XPages techniques, which may be easier for anyone not well versed in JQuery.
I would never recommend using a table for layout purposes, use <div> and or <ul/li> tags and use CSS. However for the purpose of answering this specific question I've used a table (climbing off soapbox now)...
So I'm assuming that the data in each row is in the same document. If so and the user is writing one observation at a time why not use a full refresh? Everything is done client-side on Unplugged (even the server parts - if that makes sense) so its fast. This method shows existing observations (if applicable) and allows the users to enter one more row at a time:
Add a hidden field on your form - NoOfObservations - this can be a text field and will be used as an index in the XPage.
Add your datasource on an XPage to the form
Create a beforePageLoad event to set the index from either an existing doc or new one:
<xp:this.beforePageLoad><![CDATA[#{javascript:
var ObservationCount = 0;
if(!document1.isNewNote()){
ObservationCount = document1.getItemValue("NoOfObservations")[0];
}
sessionScope.put("observations", ObservationCount);
}]]>
</xp:this.beforePageLoad>
Create your table with existing rows - using the xp:repeat Control with the index in the sessionScope to produce your rows - adding 2 new lines for new entries:
<table>
<xp:repeat id="repeat1" rows="30" var="rowData"
indexVar="dataRows">
<xp:this.value><![CDATA[#{javascript:sessionScope.get("observations");}]]></xp:this.value>
<tr>
<td>
<xp:label value="Observation" id="label1"
style="color:rgb(255,255,255)">
</xp:label>
</td>
<td>
<xp:text escape="true" id="computedField1">
<xp:this.value><![CDATA[#{javascript:return document1.getItemValueString("ObservationTitle" + dataRows);}]]></xp:this.value>
</xp:text>
</td>
</tr>
<tr>
<td>
<xp:label value="Details" id="label2"
style="color:rgb(255,255,255)">
</xp:label>
</td>
<td>
<xp:text escape="true" id="computedField2">
<xp:this.value><![CDATA[#{javascript:return document1.getItemValueString("ObservationDesc" + dataRows);}]]></xp:this.value>
</xp:text>
</td>
</tr>
</xp:repeat>
<tr>
<td>
<xp:label value="New Observation"
id="newObservationTitle" style="color:rgb(255,255,255)">
</xp:label>
</td>
<td>
<xp:inputText id="inputText1"></xp:inputText>
</td>
</tr>
<tr>
<td>
<xp:label value="New Description"
id="newObservationDesc" style="color:rgb(255,255,255)">
</xp:label>
</td>
<td>
<xp:inputTextarea id="inputTextarea1"></xp:inputTextarea>
</td>
</tr>
</table>
Use an xp:button with the following SSJS in the onClick event:
<xp:button value="Save" id="button1">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action>
<xp:actionGroup>
<xp:executeScript>
<xp:this.script><![CDATA[#{javascript:
var newObservation = parseInt(sessionScope.get("observations")) +1;
document1.replaceItemValue("ObservationTitle" + newObservation, getComponent("inputText1").getValue());
document1.replaceItemValue("ObservationDesc" + newObservation, getComponent("inputTextarea1").getValue());
document1.replaceItemValue("NoOfObservations", newObservation);
document1.save();}]]></xp:this.script>
</xp:executeScript>
<xp:openPage name="/UnpMain.xsp"></xp:openPage>
</xp:actionGroup>
</xp:this.action>
</xp:eventHandler>
</xp:button>
As with your other question, again I would be inclined to do this client side rather than trying to do partial refreshes. Simply add rows to the table using jQuery and then store the values from each row into hidden list fields when saving the document.
Also again, for the IDs that you want to work with, rather than rely on the IDs being generated by the server, add your own attributes and select items using those instead.