On my XPage, I have a series of search filters (State, First Name and Last Name). The search button triggers the population of the datatable which is bound to a Managed Bean.
The datatable initially gets populated with rows of data. However when I click on Next (in the pager), instead of loading the next 5 rows, it returns no more data. I KNOW the datatable is getting the right number of rows because if I change the Repeat limit in the datatable to 10, they all display. Also, the number of pages in the pager is 2 (which is the correct number).
Any ideas what could be causing this?
Thanks,
Dan
Code for the datatable:
<xp:dataTable rows="5" id="studentTable" var="currentStudent"
style="width:400.0px" value="#{studentlist.students}">
<xp:column id="firstnameColumn" style="font-weight:bold">
<xp:this.facets>
<xp:span xp:key="header">
<xp:span style="font-weight:bold">
First Name
</xp:span>
</xp:span>
</xp:this.facets>
<xp:text escape="true" id="firstnameField"
value="#{currentStudent.firstname}">
</xp:text>
</xp:column>
<xp:column id="column1">
<xp:text escape="true" id="middleinitialField"
value="#{currentStudent.middleName}">
</xp:text>
<xp:this.facets>
<xp:span xp:key="header">
<xp:span style="font-weight:bold">
Middle Name
</xp:span>
</xp:span>
</xp:this.facets>
</xp:column>
<xp:column id="lastnameColumn" style="font-weight:bold">
<xp:this.facets>
<xp:span xp:key="header">
<xp:span style="font-weight:bold">
Last Name
</xp:span>
</xp:span>
</xp:this.facets>
<xp:text escape="true" id="lastnameField"
value="#{currentStudent.lastname}">
</xp:text>
</xp:column>
<xp:column id="idColumn">
<xp:this.facets>
<xp:span xp:key="header">
<xp:span style="font-weight:bold">ID</xp:span>
</xp:span>
</xp:this.facets>
<xp:text escape="true" id="computedField1"
value="#{currentStudent.id}">
</xp:text>
</xp:column>
<xp:this.facets>
<xp:pager layout="Previous Group Next" xp:key="header"
id="pager1" for="studentTable" partialRefresh="true">
</xp:pager>
</xp:this.facets></xp:dataTable>
Code behind the button:
var state=getComponentValue('state');
var firstName=document1.getItemValueString("firstName");
var lastName=document1.getItemValueString("lastName");
if(state == "--" || firstName == "" || lastName == "")
{
//do nothing
}
else{
studentlist.setConnDB("jdbc:sqlserver://XX.XX.X.XX:1433;DatabaseName=dan_test");
studentlist.setConnUserName("test");
studentlist.setConnPassword("Password1");
studentlist.setSQLQuery("SELECT FirstName,MiddleName,LastName,ID FROM TestStudents WHERE FirstName Like '"+firstName+"%' AND LastName Like '"+lastName+"%' AND State = '"+state+"' ORDER BY LastName ASC");
I'm making some assumptions here without seeing your MBean code, but hope to point you in the right direction nonetheless...
Ensure you are restoring the datamodel within the studenLists bean during invocations of the getStudents() call. I see you have value="#{studentlist.students}" in the XSP fragment above... that should map to a getStudents() method on the bean returning a DataModel of Student objects or the like... presumably yes? So, a couple of things to bear in mind here... ensure the bean is in at least viewScope (preferably) and that you "restore the wrapped data" in the getStudents() method based on a non-transient buffer (eg: ArrayList that is holding the current set of filtered "student" objects - and make sure that the buffer of Student objects is serializable - critical for "Disk Persistence"!
If you use this approach, the Pager will always be "paging" against an up-to-date restored datamodel for each subsequent paging request posted against the current view - the page index etc being internally managed by the DataModel object.
eg:
1. No restoration:
...
private transient DataModel studentSearchResults;
private transient List searchResults;
...
public DataModel getStudents() {
if (null == studentSearchResults) {
studentSearchResults = new ListDataModel();
}
return studentSearchResults;
}
versus:
2. With restoration:
...
private transient DataModel studentSearchResults;
private List searchResults;
...
public DataModel getStudents() {
if (null == studentSearchResults) {
studentSearchResults= new ListDataModel();
if(null != searchResults){
studentSearchResults.setWrappedData(searchResults);
}
}
return studentSearchResults;
}
Do you also lose the data if you add another button that just partially refreshes the dataTable? That should identify if it's a problem with managed bean properties being reset (in which case the contents of the dataTable would disappear) or a problem with paging (in which case they wouldn't).
Related
I have a repeat control which displays a list of Attachments, which are saved as response documents to the current document. Right now I just see all the main docs and response docs in a list. How do I 1) Filter the view to only to include the response docs and not main docs? 2) Filter the view to only include responses to the current document?
I tried using the Filter by column value on the datasource, but I can't figure it out.
My Xpage datasources are "document1" document and "Requirements" view
<xp:dominoView var="Atts" viewName="Requirements">
</xp:dominoView>
<xp:repeat id="AttsContainer" rows="100"
var="Attachments" repeatControls="true" value="#{Atts}">
<xp:panel id="AttsPanel">
<xp:table style="width:100.0%;border-width:thin;border-color:rgb(0,0,0);border-style:solid">
<xp:tr>
<xp:td style="width:234.0px">
<xp:text escape="true"
id="computedField1" value="#{Attachments.AttName}">
</xp:text>
</xp:td>
<xp:td><xp:text escape="true" id="computedField2">
<xp:this.value><![CDATA[#{javascript:var doc:NotesDocument = Attachments.getDocument();
var sUNID = doc.getUniversalID();
sUNID}]]></xp:this.value>
</xp:text></xp:td>
<xp:td>
<xp:link escape="true"
id="link2">
<xp:this.text><![CDATA[#{javascript:Attachments.getColumnValue("Files")}]]></xp:this.text>
<xp:this.value><![CDATA[#{javascript:var doc:NotesDocument = Attachments.getDocument();
var sUNID = doc.getUniversalID();
ATT = Attachments.getColumnValue("Files");
"/bid.nsf/0/" + sUNID + "/$FILE/" + ATT}]]></xp:this.value></xp:link></xp:td>
</xp:tr>
</xp:table></xp:panel>
From the top of my head:
Create a view showing only the documents created with the response's form.
Disable the "show response documents in a hierarchy" property
Add a categorized first column with this formula: #Text($ref)
That gives you a view where all responses are categorized by their parents unid. You use that view in the data source and set the categoryFilter the the unid of the main document.
I'm trying to hide a column in the dataTable, but not working. Hiding only header. In example I try hide first column. Domino 9.0.1.
I was mistaken or is it a bug?
I know about the "display: none" for the column, but I have a lot of data, and not rendered column save my local performance
<xp:dataTable id="dataTable1" rows="30" var="item">
<xp:this.value><![CDATA[#{javascript:var collection = [];
for( var i = 0; i < 10; i++ ) {
var obj = {};
obj.name = "Name" + i;
obj.key = "Key" + i;
collection.push( obj );
}
return collection;}]]></xp:this.value>
<xp:column id="column1" rendered="false">
<xp:this.facets>
<xp:span xp:key="header">Name</xp:span>
</xp:this.facets>
<xp:text escape="true" id="computedField1"
value="#{item.name}">
</xp:text>
</xp:column>
<xp:column id="column2">
<xp:this.facets>
<xp:span xp:key="header">Key</xp:span>
</xp:this.facets>
<xp:text escape="true" id="computedField2" value="#{item.key}"></xp:text>
</xp:column>
</xp:dataTable>
I can confirm I get exactly the same behavior out of this. I agree that I would expect the entire column to not render, but that's apparently not its behavior. I've always viewed the xp:dataTable control as a slightly dressed up xp:repeat, but not with as much window dressing as an xp:viewPanel control and the Domino Designer wiki page on xp:dataTable isn't exactly helpful for any of the specifics and my Mastering XPages 2nd ed. also doesn't go too far into detail on xp:dataTable.
It's also worth noting that also setting the rendered property on your xp:text field still builds out the first cell in each row, regardless of the first <th> being suppressed. I'm guessing there's something in the way the xp:dataTable control builds out the HTML elements that's funky.
My recommendation would be to switch to an xp:repeat control, which will give you more control over the entire process. Adapting your code, it would go somewhat like this:
<xp:table>
<xp:tr>
<xp:td>Name</xp:td>
<xp:td>Key</xp:td>
</xp:tr>
<xp:repeat
id="repeat1"
rows="30"
var="item">
<xp:this.value><![CDATA[#{javascript:var collection = [];
for( var i = 0; i < 10; i++ ) {
var obj = {};
obj.name = "Name" + i;
obj.key = "Key" + i;
collection.push( obj );
}
return collection;}]]></xp:this.value>
<xp:tr>
<xp:td
rendered="false">
<xp:text
escape="true"
id="computedField1"
value="#{item.name}">
</xp:text>
</xp:td>
<xp:td>
<xp:text
escape="true"
id="computedField2"
value="#{item.key}">
</xp:text>
</xp:td>
</xp:tr>
</xp:repeat>
</xp:table>
Do note that to also set the rendering property of the cell being used as the header label (in place of a <th> it will render as a <td>), you'll need to manage that property visibility separately.
I have a repeat control which has its source as a notesdocumentcollection. Inside the repeat control I have a panel that has a datasource and the document id property of the datasource is set to the universal id of the a single document which in my case is the repeat control variable apprDoc (apprDoc.getUniversalID). Thus using the panel datasource I am trying to directly bind each field in the repeat control to the respective back end document. I have a submit button on click of which I need to post the values selected by the user into the backend document and also set some other fields which are not bound to the back end document. I need to get a handle to each document in the document collection in order to set a field value on the backend document. Can someone please suggest a way to do the same, or any other way to achieve the functionality which I am trying to achieve. Please find below the code for the same:
<xp:repeat id="repeat1" rows="30" var="apprDoc" indexVar="docIndex"
first="0">
<xp:this.value><![CDATA[#{javascript:// Look up the employee details view to get the employee appraisal details from the current database
var curDB:NotesDatabase = session.getCurrentDatabase();
var vwlkApprView:NotesView = curDB.getView("vwlkAllApprApper");
var collDocAppr:NotesDocumentCollection = vwlkApprView.getAllDocumentsByKey(sessionScope.EmployeeID);
if(collDocAppr.getCount() != 0){
return collDocAppr;
}
return null;}]]></xp:this.value>
<xp:panel id="pnlFG">
<xp:this.data>
<xp:dominoDocument formName="frmAppraisal" var="appraisalDoc"
action="editDocument" documentId="#{javascript:apprDoc.getUniversalID();}">
<xp:this.querySaveDocument><![CDATA[#{javascript:var apprDoc:NotesDocument = appraisalDoc.getDocument();
if(requestScope.SubmitFG == true){
apprDoc.replaceItemValue("CurrFGRRStatus","1");
apprDoc.save();
}}]]></xp:this.querySaveDocument>
</xp:dominoDocument>
</xp:this.data>
<xp:tr>
<xp:td styleClass="tdCls" style="width:2%">
<xp:label id="SrNo">
<xp:this.value><![CDATA[#{javascript:var index = parseInt(docIndex)
index = index + 1;
index.toString();}]]></xp:this.value>
</xp:label>
</xp:td>
<xp:td styleClass="tdCls" style="width:20.0%">
<xp:label id="ApeName">
<xp:this.value><![CDATA[#{javascript:return apprDoc.getItemValueString("AppraiseeName");}]]></xp:this.value>
</xp:label>
</xp:td>
<xp:td styleClass="tdCls" style="width:15.0%">
<xp:label id="ApeGrade">
<xp:this.value><![CDATA[#{javascript:return apprDoc.getItemValueString("Appraisee_Grade");}]]></xp:this.value>
</xp:label>
</xp:td>
<xp:td styleClass="tdCls" style="width:15.0%">
<xp:div style="text-align:center">
<xp:label id="appeTotImpRate"
style="font-size:10pt;color:rgb(255,0,0);font-weight:bold">
<xp:this.value><![CDATA[#{javascript:return apprDoc.getItemValueDouble("AppeTotImpRate").toFixed(2);}]]></xp:this.value>
</xp:label>
</xp:div>
</xp:td>
<xp:td styleClass="tdCls" style="width:15.0%">
<xp:div style="text-align:center">
<xp:label id="apprTotImpRate"
style="color:rgb(255,0,0);font-size:10pt;font-weight:bold">
<xp:this.value><![CDATA[#{javascript:return apprDoc.getItemValueDouble("ApprTotImpRate").toFixed(2);}]]></xp:this.value>
</xp:label>
</xp:div>
</xp:td>
<xp:td styleClass="tdCls" style="width:15.0%">
<xp:div style="text-align:center">
<xp:label id="revTotImpRate"
style="font-size:10pt;color:rgb(255,0,0);font-weight:bold">
<xp:this.value><![CDATA[#{javascript:return apprDoc.getItemValueDouble("RevTotImpRate").toFixed(2);}]]></xp:this.value>
</xp:label>
</xp:div>
</xp:td>
<xp:td styleClass="tdCls" style="width:10.0%">
<xp:div style="text-align:center">
<xp:comboBox id="appeGrades"
value="#{appraisalDoc.ApperFinalGrade}" style="width:50.0px"
disableClientSideValidation="true">
<xp:this.validators>
<xp:validateRequired>
<xp:this.message><![CDATA[#{javascript:var index = parseInt(docIndex)
index = index + 1;
"Please select a final grade in row number " + index.toString();}]]></xp:this.message>
</xp:validateRequired>
</xp:this.validators>
<xp:selectItem itemLabel="-"
itemValue="">
</xp:selectItem>
<xp:selectItem itemLabel="1"
itemValue="1">
</xp:selectItem>
<xp:selectItem itemLabel="2"
itemValue="2">
</xp:selectItem>
<xp:selectItem itemLabel="3"
itemValue="3">
</xp:selectItem>
<xp:selectItem itemLabel="4"
itemValue="4">
</xp:selectItem>
<xp:selectItem itemLabel="5"
itemValue="5">
</xp:selectItem>
</xp:comboBox>
</xp:div>
</xp:td>
</xp:tr>
</xp:panel>
</xp:repeat>
what about a button with the simple action "save data sources"? That should save all data sources available on the XPage.
The computation of the repeat elements isn't very efficient - the JSF lifecycle will make you lookup the values twice. You could try this instead:
// If we looked them up before we don't need to do anything
if (viewScope.approveIDs) {
return viewScope.approveIDs;
}
// Look up the employee details view to get the employee appraisal details from the current database
var curDB:NotesDatabase = session.getCurrentDatabase();
var vwlkApprView:NotesView = curDB.getView("vwlkAllApprApper");
var collDocAppr:NotesViewEntryCollection = vwlkApprView.getAllEntriesByKey(sessionScope.EmployeeID);
// Don't do a count, slow!
var result = new java.util.Vector();
// Add all unids
var ve:NotesViewEntry = collDocAppr.getFirstEntry();
while (ve != null) {
var nextVe = ve.getNextEntry(ve);
result.add(ve.getUniversalId()); // eventually check to avoid categories?
ve.recyle();
ve = nextVe;
}
viewScope.approveIDs = result.isEmpty() ? null : result;
collDocAppr.recyle();
vwlkApprView.recycle();
return viewScope.approveIDs;
This code (off my memory, so check syntax) has a few notable properties:
Error handling is missing, so please add try { } catch(e ) { } generously
.recyle() is used to release memory
ViewEntryCollection instead of DocumentCollection -> better performance
UNIDs are cached in a Vector, so this needs to run only once
the variable apprDoc now directly contains the unid, saving a document call in the panel
the variable docIndex can be used with viewScope.approveIds.get() to get access to the ID too
you could easily extend the example instead of Stings in the vector use a custom object (bean) and get rid of the data sources (might eventually help performance) if you get all fields into the view, so you can continue to use the viewNavigator
I am using a FileDownload control to show a list of attachments from a Notes document.
The dates are shown as: dd/MM/yy hh.mm - and since my users are Danish I would like to show the dates in the format: dd-MM-yyyy hh:mm.
That is pretty simple if you use a SimpleDateFormater. In SSJS that would look like:
var date:Date = new Date(aFile.getCreated());
if(date==null) return date;
return new java.text.SimpleDateFormat("dd-MM-yy hh:mm").format(date);
In the FileDownload control you can compute the value of "createdValue" property. However, the control expects an object of type "Date" which does not allow for the formating in the above example (it just throws an Error 500).
Any suggestions?
/John
You already found that the createdValue property expects a java.util.Date object, so the only way to change how that is formatted is by changing the browser locale used by the XPage (as answered here). Using the standard download control you can get a handle on every file by adding the var="file" attribute to it and use that in the createdValue property:
createdValue="new Date(file.getCreated());"
If you want to have more control on the list of files you could use a repeat control and bind it to the list of files from a RichText item:
<xp:repeat id="repeat1" rows="30" var="file" disableOutputTag="true">
<xp:this.value><![CDATA[#{javascript:document1.getAttachmentList("files")}]]></xp:this.value>
<xp:this.facets>
<xp:text escape="false" disableTheme="true" xp:key="header">
<xp:this.value><![CDATA[<table><tbody>]]>
</xp:this.value></xp:text>
<xp:text escape="false" disableTheme="true" xp:key="footer"><xp:this.value><![CDATA[</tbody></table>]]></xp:this.value></xp:text>
</xp:this.facets>
<tr>
<td>
<xp:text escape="true" id="computedField1" value="#{file.name}"></xp:text>
</td>
<td>
<xp:text escape="true" id="computedField2">
<xp:this.value><![CDATA[#{javascript:var date:Date = new Date(file.getCreated());
return new java.text.SimpleDateFormat("dd-MM-yy hh:mm").format(date);}]]></xp:this.value>
</xp:text>
</td>
</tr>
</xp:repeat>
By the way: another a disadvantage of the default download control is that it adds a content-disposition header to every link, so the browsers always asks you if you want to save or open the file instead of opening images (for instance) directly.
I have a RadComboBox that I am using to display department name and abbreviations. I am using an Item Template with a LinqDataSource to make each item appear as:
DeptAbbr - (DeptName)
Here is the code I am using to do this and it works fine:
<telerik:RadComboBox ID="rcbDepartments" runat="server" AppendDataBoundItems="True"
OnInit="rcbDepartments_Init" DataTextField="DepartmentAbbr" AutoPostBack="True"
DataSourceID="ldsDepartments" DataValueField="DepartmentID" HighlightTemplatedItems="true"
NoWrap="true" Width="250px">
<ItemTemplate>
<div>
<b>
<%# Eval("DepartmentAbbr")%></b><%# Eval("DepartmentName", " - ({0})") %>
</div>
</ItemTemplate>
</telerik:RadComboBox>
My question is this. I want to add an initial item in the list that is for "All Departments" and is the default item. I can do this easily, but the problem I'm having is that because I am not storing an "All Departments" entry in the database, the templating shows a blank space at the beginning of the items list when you pull down the combo box. I'm trying to find out if there is any way to template all but first item in the list?
Note: I have also tried do a conditional in the Eval like this:
<b><%# (Eval("DepartmentAbbr") != null) ? Eval("DepartmentAbbr") : "All Departments" %></b><%# Eval("DepartmentName", " - ({0})") %>
But it only evaluates on the items that are databound and not the initial item which I am sticking in manually. In other words, if I change the above statement to be:
<b><%# (Eval("DepartmentAbbr") == null) ? Eval("DepartmentAbbr") : "All Departments" %></b><%# Eval("DepartmentName", " - ({0})") %>
Then I just get a list with one blank item at the top and the rest reading "All Departments".
My work around for this problem has been to do some funky selection stuff with LINQ server side, but that has forced me to get rid of all templating and html formatting.
You can define the 'All Departments' RadComboBoxItem as a static item in the <Items> collection. Since you have enabled the AppendDataBoundItems property, you don't want to bind to your data source until after the control has already bound the static items; otherwise you'll get the blank space you are seeing when expanding the combo box. Also, use DataBinder.Eval(Container, "Text") to render the DepartmentAbbr field. Since you have set this field as the DataTextField for the control, that value will always render. If not, you'll get the empty space again when the control binds to the static item because it doesn't know what DepartmentAbbr is; it only has a Text field. Here's an example to get you going:
<telerik:RadComboBox ID="RadComboBox1" runat="server"
AppendDataBoundItems="True"
DataTextField="Abbr"
AutoPostBack="True"
DataValueField="DeptID"
HighlightTemplatedItems="true"
NoWrap="true"
Width="250px">
<Items>
<telerik:RadComboBoxItem runat="server" Text="All Departments" />
</Items>
<ItemTemplate>
<div>
<b><%# DataBinder.Eval(Container, "Text")%></b><%# Eval("Name", " - ({0})") %>
</div>
</ItemTemplate>
</telerik:RadComboBox>
public partial class _Default : System.Web.UI.Page
{
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
RadComboBox1.Load += new EventHandler(RadComboBox1_Load);
}
protected void RadComboBox1_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
// Ensure the static items are already bound before assigning
// new data to the DataSource property
RadComboBox1.DataBind();
var departments = new[] {
new { DeptID = 1, Abbr = "ACME", Name = "ACME Corporation" },
new { DeptID = 2, Abbr = "MSFT", Name = "Microsoft Corporation" },
new { DeptID = 3, Abbr = "GOOG", Name = "Google, Inc" }
};
RadComboBox1.DataSource = departments;
RadComboBox1.DataBind();
}
}
}
Hope that helps!