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.
Related
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>
I have a categorized view that I am using as a view data source in an XPage. I have set the categoryFilter of the data source so that only the call history of the selected individual (for a given incident) is displayed. The filter being stored in the sessionScope is a concatenation of the UniqueID of the person and the IncidentID. The lookup view is categorized by that value.
One would expect that to work, however the filtering does not seem to be working and I can see all call histories regardless of the person I choose. I've done this before and I must be missing something obvious.
I have verified the sessionScope.callHistoryID is being updated as I select a new person. My Call History db currently only has 2 history documents for one guest, yet they display for every guest in the Xpage.
sessionScope.callHistoryID from the debugger is obviously not the same (see below):
<xp:panel id="pnlCallHistoryModal" style="padding-bottom:20px">
<xp:this.data>
<xp:dominoView var="callhistoryView"
viewName="luCallHistorybyPaxandIncidentID"
databaseName="blah.nsf"
ignoreRequestParams="true"
categoryFilter="#{javascript:sessionScope.callHistoryID}">
</xp:dominoView>
</xp:this.data>
<div class="col-xs-12 col-md-12 col-lg-12 panel-body">
<xp:repeat id="repeat1" value="#{callhistoryView}"
var="viewRow" indexVar="rowIndex" rows="500"
repeatControls="false">
<xp:this.facets>
<xp:text disableTheme="true" xp:key="header"
escape="false">
<xp:this.value><![CDATA[
<table class="table table-hover">
<tbody>]]></xp:this.value>
</xp:text>
<xp:text disableTheme="true" xp:key="footer"
escape="false">
<xp:this.value><![CDATA[
</tbody>
</table>]]></xp:this.value>
</xp:text>
</xp:this.facets>
<xp:tr>
<xp:this.rendered><![CDATA[# {javascript:if(viewRow.isCategory()){
return false
}else{
return true
}}]]></xp:this.rendered>
<xp:td styleClass="col-md-10 col-lg-10 small">
<xp:text escape="true" id="name">
<xp:this.value><![CDATA[#{javascript:#ProperCase(viewRow.getColumnValue("CallerFullName"))}]]></xp:this.value>
</xp:text>
<br></br>
<xp:text escape="true" id="computedField1"
styleClass="small secondary-text">
<xp:this.value><![CDATA[#{javascript:if(viewRow.getColumnValue("CallerMemo").length > 128){
return viewRow.getColumnValue("CallerMemo").substring(0,128) + " ..."
}else{
return viewRow.getColumnValue("CallerMemo")
}}]]></xp:this.value>
</xp:text>
</xp:td>
<xp:td styleClass="col-md-2 col-lg-2 small">
<xp:text escape="true" id="creationdatetime"
styleClass="small">
<xp:this.value><![CDATA[#{javascript:viewRow.getColumnValue("CallDateTime")}]]></xp:this.value>
<xp:this.converter>
<xp:convertDateTime type="both"
dateStyle="short" timeStyle="short">
</xp:convertDateTime>
</xp:this.converter>
</xp:text>
</xp:td>
</xp:tr>
</xp:repeat>
</div>
</xp:panel>
I suspect this will be doing the equivalent of ViewNavigator.createViewNavFromCategory(). It's worth checking how that acts if the category name doesn't exist. It may fall back to a partial match.
For "restrict to category", you might be better placed using the keys property and settings keysExactMatch to true.
In my dynamic view panel, I need to have sortable columns. Problem is when sorting a column it resubmits the XPage and whole content is refreshed. Is there a way to achieve sorting only on the dynamic view panel without submitting whole page? Also, I want to hide this control if selected view is empty.
Here is my code:
<xp:panel id="searchPanel">
<xp:table styleClass="table table-condensed" style="width:auto">
<xp:tr>
<xp:td style="border-top:0px" styleClass="text-nowrap" id="searchColumn">
<xp:inputText id="inputText1" value="#{viewScope.searchterm}" styleClass="form-control"></xp:inputText>
<xp:link escape="true" text="" id="searchLink">
<i class="fa fa-search" style="margin-left:5px"></i>
<xp:eventHandler event="onclick" refreshMode="partial" immediate="false" submit="true" refreshId="ddPanel">
<xp:this.action><![CDATA[#{javascript:var pager:com.ibm.xsp.component.xp.XspPager = getComponent("pager");
pager.gotoFirst();}]]></xp:this.action>
</xp:eventHandler>
</xp:link>
<xp:link escape="true" text="" id="clearSearchLink">
<i class="fa fa-times" style="margin-left:5px"></i>
<xp:eventHandler event="onclick" submit="true" refreshMode="partial" refreshId="ddPanel" onComplete="showHideTableToggle()">
<xp:this.action><![CDATA[#{javascript:viewScope.remove("searchterm");}]]></xp:this.action>
<xp:this.script><![CDATA[dojo.byId(getID("inputText1")).value=""]]></xp:this.script>
</xp:eventHandler>
</xp:link>
</xp:td>
<xp:td style="border-top:0px;padding-left:20px">
<xp:link escape="true" id="addButton" onclick="return false;" styleClass="btn btn-primary" style="color:rgb(255,255,255)" text="Add">
<i class="fa fa-plus" style="margin-right:5px"></i>
<xp:eventHandler event="onclick" submit="false" refreshMode="complete">
<xp:this.script><![CDATA[XSP.openDialog(getID('contentDialog'),'',{"docID":"","newDoc":"false"});]]></xp:this.script>
</xp:eventHandler>
</xp:link>
</xp:td>
</xp:tr>
</xp:table>
<xp:panel id="ddPanel">
<xe:dynamicViewPanel id="mainView" width="100%" dataTableStyleClass="table table-bordered table-condensed table-hover" rows="10" var="myView"
showColumnHeader="true" customizerBean="com.hcl.igdm.PickerViewBeanMainViews" partialRefresh="true" refreshId="ddPanel"
>
<xe:this.data>
<xp:dominoView var="mainDataView" dataCache="nodata">
<xp:this.viewName><![CDATA[#{javascript:sessionScope.showView.split("~")[0]}]]></xp:this.viewName>
</xp:dominoView>
</xe:this.data>
<xe:this.rowAttrs>
<xp:attr name="onClick">
<xp:this.value><![CDATA[#{javascript:return "javascript:myJSFunction();"}]]></xp:this.value>
</xp:attr>
</xe:this.rowAttrs>
</xe:dynamicViewPanel>
<xp:pager layout="Previous Group Next" partialRefresh="true" id="pager" for="mainView" title="Pager"></xp:pager>
</xp:panel>
It's a long time ago this question was placed... but maybe the answer can help other people.
You can put the dynamicViewPanel into a 'dynamic content' container (from ExtLib) with partial events attribute set to true. This will convert the full update events into partial refresh events.
<xe:dynamicContent partialEvents="true">
...
</xe:dynamicContent>
I am trying to create a repeat element from a view in a different database that does not allow Anonymous ACL access.
PubDb.nsf has ACL set for user Anonymous to Author
PrivDb.nsf has ACL set for user Anonymous and Default "no access".
The database, PrivDb has information that I want to secure, but would like to pull a couple of views out for public display.
When I create a repeat in PubDb.nsf using PrivDb.nsf view, It will not display any data. I am able to use sessionAsSigner to get SSJS to see the view and open documents at the server side level, but when I try to display the view or repeat elements there is no data ? If I change ACL in PrivDb for Anonymous to Reader everything works, but now the entire db is open to Anonymous access.
I understand that sessionAsSigner and sessionAsSignerWithFullAccess allows me to use db signers effective rights, and I can use SSJS to access the notesdocuments and publish data using computed fields for individual docs, but I can't find any information that tells me I can or can't display an xpages element (repeat or view) using the sessionAsSigner. Maybe I can create a lotusscript agent that populates the block?
Below is the code I use to create the repeat element in PubDb.nsf. Note, I assigned the DB twice, once in Application and again in View because when I didn't use sessionAsSigner in the view's computed value I would get prompted for authentication, and it's required in the app field.
<xp:this.data>
<xp:dominoView var="view2">
<xp:this.databaseName><![CDATA[${javascript:
var DB:NotesDatabase=sessionAsSigner.getDatabase(database.getServer(),"PrivDb.nsf");
DB;
}]]>
</xp:this.databaseName>
<xp:this.viewName><![CDATA[${javascript:
var dbOther:NotesDatabase = sessionAsSignerWithFullAccess.getDatabase(database.getServer(), "PrivDb.nsf");
var lookupView:NotesView = dbOther.getView( "PrivView" );
lookupView.recycle();
lookupView}]]>
</xp:this.viewName>
</xp:dominoView>
</xp:this.data>
<div class="container">
<div class="page-header">
<h1>This is a test.</h1>
</div>
<xp:br></xp:br>
<xp:br></xp:br>
<div class="row">
<div class="col-md-1"></div>
<div class="col-md-10">
<xp:repeat id="repeat1" rows="30" var="playerData"
value="#{view2}" repeatControls="true">
<div class="panel panel-default">
<xp:text escape="true" id="computedField1"
value="#{playerData.$5}">
</xp:text>
hcp:
<xp:text escape="true" id="computedField5"
value="#{playerData.$6}">
</xp:text>
<xp:br></xp:br>
<xp:text escape="true" id="computedField2"
value="#{playerData.$7}">
</xp:text>
<xp:text escape="true" id="computedField3"
value="#{playerData.$8}">
</xp:text>
<xp:text escape="true" id="computedField4"
value="#{playerData.$9}">
</xp:text>
<xp:br></xp:br>
</div>
<div class="col-md-1"></div>
</xp:repeat>
</div>
</div>
</div>
Try changing your repeat to return for instance the view entries directly using sessionAsSigner instead of going through a view data source. So in your case:
<xp:repeat id="repeat1" rows="30" var="playerData">
<xp:this.value><![CDATA[#{javascript:
sessionAsSigner.getDatabase(database.getServer(), "PrivDb.nsf").getView("PrivView").getAllEntries();
}]]></xp:this.value>
...
</xp:repeat>
I'm using teamstudio's unplugged for the record.
I'm trying to build a table using a repeat control with a view as a data source.
The user should be able to change edit the documents and then save all data sources.
Below is a example that currently does not work.
I have also tried using computed ids for all the different check-boxes in the cells and
storing the UNID of the row's document in a hidden text field and then read every row based on the computed ids and saving them. That didn't work because computing ids in a repeater is impossible in unplugged despite tons of effort.
If i have done something wrong or there is a trick i haven't tried, i would love to hear about it.
<xp:table id="table">
<xp:tr>
<xp:td>Document name</xp:td>
<xp:td>Field1</xp:td>
<xp:td>Field2</xp:td>
<xp:td>Field3</xp:td>
</xp:tr>
<xp:repeat id="repeat1" rows="30"
value="#{TestDocs}" indexVar="index" var="Docs" first="0"
removeRepeat="true" repeatControls="true">
<xp:panel>
<xp:tr>
<xp:td>
<xp:text id="Name" escape="true"
value="#{document1.Name}">
</xp:text>
</xp:td>
<xp:td>
<xp:checkBox id="Field1"
checkedValue="true" uncheckedValue="false"
value="#{document1.Field1}">
<xp:this.defaultChecked><![CDATA[${javascript:
if(document1.getDocument().getItemValue("Field1")=="[true]"){
return true;
}else{
return false;
}}]]></xp:this.defaultChecked>
</xp:checkBox>
</xp:td>
<xp:td>
<xp:checkBox id="Field2"
checkedValue="true" uncheckedValue="false"
value="#{document1.Field2}">
<xp:this.defaultChecked><![CDATA[${javascript:
if(document1.getDocument().getItemValue("Field2")=="[true]"){
return true;
}else{
return false;
}}]]></xp:this.defaultChecked>
</xp:checkBox>
</xp:td>
<xp:td>
<xp:checkBox id="Field3"
checkedValue="true" uncheckedValue="false"
value="#{document1.Field3}">
<xp:this.defaultChecked><![CDATA[${javascript:
if(document1.getDocument().getItemValue("Field3")=="[true]"){
return true;
}else{
return false;
}}]]></xp:this.defaultChecked>
</xp:checkBox>
</xp:td>
</xp:tr>
</xp:panel>
</xp:repeat>
</xp:table>
<xp:button value="Save" id="button3"
styleClass="button">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete" disableValidators="true">
<xp:this.action>
<xp:save></xp:save>
</xp:this.action>
</xp:eventHandler>
</xp:button>
My inclination would be to move the logic for all of this client side. So build the table with jQuery, and then on save, take the values out of the fields you create and write them into hidden fields that will actually get saved.
In terms of computing IDs of fields etc, I would add attributes to the elements you want to work with and then select them using jQuery:
$('[myattr="the value"]')