LazyDataModel initial load called multiple times - primefaces

I have this datatable which I implemented with Primefaces's LazyDataModel.
Everything works fine except for the initial load when I open the page for the first time or refresh.
The load method get called 6-7 times.
When I change page, filter or sort it works correctly, load is only called once.
Since I perform calls to the database I would like to prevent it to be called more then needed.
I have the following:
XHTML:
<p:outputPanel id="documentsPanel">
<h:form id="documentsForm">
<p:dataTable id="documentsTable"
widgetVar="documentsTable"
var="doc"
value="#{MyManagedBean.model}"
paginator="true" rows="20"
paginatorTemplate="{FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {CurrentPageReport}"
currentPageReportTemplate="{totalRecords} #{c.NumberOfRequestFound}"
lazy="true">
...
</p:dataTable>
</h:form>
</p:outputPanel>
Java:
public class DocumentEncodingDataModel extends LazyDataModel<SelectableDocumentToBeEncodedDTO> {
#Override
public List<SelectableDocumentToBeEncodedDTO> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, Object> filters) {
....
}
#Override
public int getRowCount() {
return super.getRowCount();
}
#Override
public SelectableDocumentToBeEncodedDTO getRowData(String rowKey) {
log.debug("getRowData");
if (!StringUtils.isBlank(rowKey)) {
for (SelectableDocumentToBeEncodedDTO doc : currentDocumentsToBeEncoded) {
if (doc.getDocumentToBeEncoded().getId().toString().equals(rowKey)) {
return doc;
}
}
}
return null;
}
#Override
public Object getRowKey(SelectableDocumentToBeEncodedDTO doc) {
return doc.getDocumentToBeEncoded().getId();
}
}
Primefaces version: 6.2
JSF version: 2.1

Related

Passing an object from p:dataTable to Controller on rowSelect in JSF

I am trying to pass an object from populated with items table to Controller. Then I use JS alert to verify success. However I fail to pass anything from the table.
Any help would be appreciated.
issues.xhtml
<p:dataTable var="issue" value="#{issuesController.findWithParameter(issues, startenddates)}" styleClass="list" selectionMode="single" selection="#{issuesController.issue}" rowKey="#{issue.id}" >
<p:ajax event="rowSelect" update="mainForm" listener ="#{issuesController.onRowSelect}"
oncomplete="alert(args.name)"/>
<p:column headerText="Id" id = "head_id" style="width:0%; padding: 0px;">
<h:outputText value="#{issue.id}"/>
</p:column>
<p:column headerText="Child" id = "head_child" style="width:12%;">
<h:outputText value="#{issue.child}"/>
</p:column>
...
</p:dataTable>
IssuesController.java
#Named("issuesController")
#RequestScoped
public class IssuesController implements Serializable {
#Inject
private Issues issue;
....
public void onRowSelect(SelectEvent event) throws IOException {
Issues i = (Issues) event.getObject();
String toWrite;
if (i == null) {
toWrite = "Item is not recieved";
} else {
toWrite = i.toString();
}
RequestContext.getCurrentInstance().addCallbackParam("name", toWrite);
}
Issues.java
#Named("issues")
#SessionScoped
#XmlRootElement
public class Issues implements Serializable {
....
}
I tried different types of scope, tried f:attribute...neither of this worked...
Try using process="#this" and change the scope to
#ViewScoped.

Primefaces initial sort order for multisort dataTable in tabView

I have a page (page1.xhtml) with a tabView and a dataTable within one of the tabs. The dataTableuses lazy loading and should provide multisort mode.
Therefore I use a List<SortMeta> for the sortBy attribute of dataTable as found here (Initial sortorder for PrimeFaces datatable with multisort).
The List<SortMeta> is created in getPreSortOrder() but findComponent() for clientId "myForm:tabs:data:colName" always returns null!
In another constellation (page2.xhtml) where I have the dataTable not in a tabView findComponent() always returns the correct column component!
So the problem seems to be the combination with tabView?!
Any hints welcome - Thank you!
page.xhtml:
<html>
<f:metadata>
<f:viewAction action="#{model.loadData}"/>
</f:metadata>
<ui:composition template="/WEB-INF/template.xhtml">
<ui:define name="content">
<h:form id="myForm">
<p:panelGrid>...</p:panelGrid>
<p:tabView id="tabs">
<p:tab id="tab1">...</p:tab>
<p:tab id="tab2">...</p:tab>
<p:tab id="tab3">
<p:dataTable id="data" lazy="true"
value="#{model.personTableModel}" var="item"
sortMode="multiple" sortBy="#{model.tableMode.preSortOrder}">
<p:column id="colName" sortBy="#{item.name}"> // <-- findComponent for "myForm:tabs:data:colName" always returns null !!!
<h:outputText value="#{item.name}"/>
</p:column>
<p:column id="colAddress" sortBy="#{item.address}">
<h:outputText value="#{item.address}"/>
</p:column>
</p:dataTable>
</p:tab>
</p:tabView>
</h:form>
</ui:define>
</ui:composition>
</html>
page2.xhtml:
<html>
<f:metadata>
<f:viewAction action="#{model.loadData}"/>
</f:metadata>
<ui:composition template="/WEB-INF/template.xhtml">
<ui:define name="content">
<h:form id="myForm">
<p:panelGrid>...</p:panelGrid>
<p:outputPanel id="tables">
<p:fieldset>
<p:dataTable id="data" lazy="true"
value="#{model.personTableModel}" var="item"
sortMode="multiple" sortBy="#{model.tableMode.preSortOrder}">
<p:column id="colName" sortBy="#{item.name}"> // <-- findComponent for "myForm:data:colName" always component
<h:outputText value="#{item.name}"/>
</p:column>
<p:column id="colAddress" sortBy="#{item.address}">
<h:outputText value="#{item.address}"/>
</p:column>
</p:dataTable>
<p:fieldset>
</p:outputPanel>
</h:form>
</ui:define>
</ui:composition>
</html>
Model.java:
#Named // javax.inject.Named
#ViewScoped // javax.faces.view.ViewScoped
public class Model implements Serializable {
private static final String COL_NAME_CLIENT_ID = "myForm:tabs:data:colName";
#Inject PersonTableDataModel personTableDataModel; // with getter & setter
public void loadData() {
List<SortMeta> preSortOrder = getPreSortOrder(COL_NAME_CLIENT_ID, "name", SortOrder.ASCENDING);
personTableDataModel.setPreSortOrder(preSortOrder);
}
private List<SortMeta> getPreSortOrder(String columnId, String sortField, SortOrder sortOrder) {
UIViewRoot viewRoot = FacesContext.getCurrentInstance().getViewRoot();
UIComponent column = viewRoot.findComponent(columnId); // <-- ALWAYS RETURNS NULL
if (Objects.isNull(column)) {
return Collections.emptyList();
}
List<SortMeta> preSortOrder = new ArrayList<>();
SortMeta sm = new SortMeta();
sm.setSortBy((UIColumn) column);
sm.setSortField(sortField);
sm.setSortOrder(sortOrder);
preSortOrder.add(sm);
return preSortOrder;
}
}
PersonTableDataModel.java:
public class PersonTableDataModel extends TableModel<Person> {
}
TableModel.java:
public class TableModel<T> extends LazyDataModel<T> {
private List<SortMeta> preSortOrder; // with getter & setter
}
I am using Primefaces 6.1 on Wildfly 10.0.0.Final
EDIT:
I added a TabChange event listener changeTab() and traversed the UIComponents and at the end the correct clientId is written to the output?!
Model.java:
public void changeTab(TabChangeEvent event) {
TabView tabView = (TabView) event.getComponent();
logger.entry(tabView.getActiveIndex());
Tab tabProzesse = tabView.findTab("myForm:tabs:tab3");
if (Objects.nonNull(tabProzesse)) {
List<UIComponent> childs = tabProzesse.getChildren();
Optional<UIComponent> c = childs.stream().filter(child -> child.getClientId().equals("myForm:tabs:data")).findFirst();
if (c.isPresent() && c.get() instanceof DataTable) {
DataTable t = (DataTable) c.get();
Optional<UIColumn> optColName = t.getColumns().stream().filter(col -> col.getClientId().contains("colName")).findFirst();
optColName.ifPresent(colName -> {
logger.debugf("colName.clientId=%s", colName.getClientId()); // <-- output colName.clientId=myForm:tabs:data:colName
});
}
}
}
I fixed it by this work-around:
added tabChange event listener to tabView and update datatable data
added binding to initial sort column with id colName
set preSortOrder for tableModel in onTabChange listener
page.xhtml:
<p:tabView id="tabs">
<p:ajax event="tabChange" listener="#{model.onTabChange}" update="data"/>
<p:tab id="tab1">...</p:tab>
<p:tab id="tab2">...</p:tab>
<p:tab id="tab3">
<p:dataTable id="data" lazy="true"
value="#{model.personTableModel}" var="item"
sortMode="multiple"
sortBy="#{model.personTableModel.preSortOrder}">
<p:column id="colName" sortBy="#{item.name}" binding="#{mode.colName}">
</p:column>
</p:dataTable>
</p:tab>
</p:tabView>
Model.java:
private UIComponent colName;
public UIComponent getColName() {
return colName;
}
public void setColName(UIComponent colNameProzess) {
this.colName = colName;
}
public void onTabChange(TabChangeEvent event) {
TabView tabView = (TabView) event.getComponent();
UIComponent uiComponent = findComponent(tabView, colName.getId());
if (Objects.nonNull(uiComponent) && uiComponent instanceof org.primefaces.component.api.UIColumn) {
List<SortMeta> preSortOrder = getPreSortOrder(uiComponent, "name", SortOrder.ASCENDING);
personTableDataModel.setPreSortOrder(preSortOrder);
}
}
private List<SortMeta> getPreSortOrder(UIColumn column, String sortField, SortOrder sortOrder) {
List<SortMeta> preSortOrder = new ArrayList<>();
SortMeta sm = new SortMeta();
sm.setSortBy(column);
sm.setSortField(sortField);
sm.setSortOrder(sortOrder);
preSortOrder.add(sm);
return preSortOrder;
}
private UIComponent findComponent(UIComponent uiComponent, String id) {
FacesContext context = FacesContext.getCurrentInstance();
UIComponent base = Objects.nonNull(uiComponent) ? uiComponent : context.getViewRoot();
final UIComponent[] found = new UIComponent[1];
base.visitTree(VisitContext.createVisitContext(context), (context1, component) -> {
if (component.getId().equals(id)) {
found[0] = component;
return VisitResult.COMPLETE;
}
return VisitResult.ACCEPT;
});
return found[0];
}

Get column value from ContextMenu of dataTable

It's easy to show the selected row value in a dialog from a p:contextMenu. However, is there an easy way to show the column value (e.g. "2:b") or to get the column index on which the right-click happend?
<p:dataTable id="dt" value="#{['a','b','c']}" var="x" selection="#{bean.x}"
widgetVar="dtv" selectionMode="single" rowKey="#{x}">
<p:column>1:#{x}</p:column>
<p:column>2:#{x}</p:column>
</p:dataTable>
<p:contextMenu for="dt">
<p:menuitem value="Show" update="di" oncomplete="PF('wv').show()" />
</p:contextMenu>
<p:dialog id="di" widgetVar="wv">X=#{bean.x}</p:dialog>
Using: PF6.0 on JavaEE 7
Just idea...
Xhtml
I added two <h:inputHidden/> components and JavaScript function applySelectedCell which is called before context menu is shown.
<h:form prependId="false">
<script type="text/javascript">
function applySelectedCell(event) {
var cell = $(event.target);
if (!cell.is('td')) {
cell = cell.closest('td');
}
var row = cell.closest('tr');
var selectedCell = row.children().index(cell);
$('#selectedCell').val(selectedCell);
$('#selectedHtmlValue').val(cell.text());
}
</script>
<h:inputHidden id="selectedCell" value="#{bean.selectedCell}"/>
<h:inputHidden id="selectedHtmlValue" value="#{bean.selectedHtmlValue}"/>
<p:dataTable id="dt" value="#{['a','b','c']}" var="x" selection="#{bean.x}"
widgetVar="dtv" selectionMode="single" rowKey="#{x}">
<p:column>1:<div style="background: yellow;">#{x}</div></p:column>
<p:column>2:#{x}</p:column>
</p:dataTable>
<p:contextMenu for="dt" beforeShow="applySelectedCell(event);">
<p:menuitem value="Show" update="di" oncomplete="PF('wv').show()" />
</p:contextMenu>
<p:dialog id="di" widgetVar="wv">
<div>Cell value (Bean) = #{bean.selectedBeanValue}</div>
<div>Cell value (HTML) = #{bean.selectedHtmlValue}</div>
</p:dialog>
</h:form>
Backing bean
#ViewScoped
#Named
public class Bean implements Serializable {
private String x = "a";
private Integer selectedCell;
private String selectedHtmlValue;
public String getX() {
return x;
}
public void setX(String x) {
this.x = x;
}
public Integer getSelectedCell() {
return selectedCell;
}
public void setSelectedCell(Integer selectedCell) {
this.selectedCell = selectedCell;
}
public String getSelectedHtmlValue() {
return selectedHtmlValue;
}
public void setSelectedHtmlValue(String selectedHtmlValue) {
this.selectedHtmlValue = selectedHtmlValue;
}
public String getSelectedBeanValue() {
if (selectedCell != null) {
return (selectedCell + 1) + ":" + x;
} else {
return x;
}
}
}

Primefaces contextmenu refresh when click on row of treetable

the situation is the following: I've got a treeTable with 3 different type of objects. The table contains the p:ajax with event="select". I wrote 3 different contextMenus, one for each type... and all works well.
My problem is that I want to enable/disable some of the menuItems; to do that I use the attribute "rendered" with condition based on properties of selected node.
All works, but only the second time I right-click on the same object (the first time the contextMenu isn't filtered).
Here is the code... (I'm including only one type of object for semplicity)
treeTable page:
<h:form id="form" prependId="false">
<p:treeTable value="#{documentsController.root}" var="document" id="docs"
selectionMode="single" selection="#{documentsController.selectedNode}">
<p:ajax event="select" process="#this" update=":form:menus"/>
<p:column headerText="#{msg['name']}">
<h:outputText value="#{document.name}" />
</p:column>
</p:treeTable>
<h:panelGroup id="menus">
<ui:include src="/menu/document_menu.xhtml"/>
</h:panelGroup>
<p:dialog id="document-rename-dialog" widgetVar="documentRenameDialog" header="#{msg.rename}">
<h:panelGrid id="doc-rename" columns="2">
<h:outputLabel for="name" value="#{msg.name}:" />
<h:inputText id="name" value="#{documentsController.name}"/>
</h:panelGrid>
<div class="spacer-10" />
<h:panelGroup layout="block">
<p:commandLink onclick="documentRenameDialog.hide();" value="#{msg.cancel}"/>
<p:commandLink actionListener="#{documentsController.renameDocument(documentsController.selectedNode.data)}"
process="#this :form:document-rename-dialog:doc-rename"
update=":form:docs"
oncomplete="documentRenameDialog.hide();"
value="#{msg.check}">
</h:panelGroup>
</common:dialog>
</h:form>
document_menu page:
<p:contextMenu id="contextMenuDocument" for="docs" nodeType="document">
<p:menuitem value="#{msg.rename}" process="#this docs" update=":form:document-rename-dialog:doc-rename"
actionListener="#{documentsController.setName(documentsController.selectedNode.data.name)}"
rendered="#{documentsController.canWrite}"
icon="ui-icon-pencil" oncomplete="documentRenameDialog.show();"/>
</p:contextMenu>
DocumentsController class:
#ManagedBean
#ViewScoped
public class DocumentsController {
private TreeNode root;
private TreeNode selectedNode;
private String name;
public TreeNode getSelectedNode() {
return selectedNode;
}
public void setSelectedNode(TreeNode selectedNode) {
this.selectedNode = selectedNode;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void renameDocument(ArterDocument selectedDocument) {
if(name != null && !name.equals("")) {
System.out.println("Document renamed in: "+name);
((MyDocument)selectedNode.getData()).setName(name);
}
else
addErrorMessage("Error renaming document.");
}
public static boolean canWrite() {
if(((MyDocument)selectedNode.getData()).isWriteable())
return true;
return false;
}
}
The MyDocument class is a simple class with a String (name) and a boolean (writeable).
Can anyone tell me how I can show filtered contextMenu at first shot?
Thank you!

set page on p:datatable

I've a datatable in primefaces and I want, when I add a row in it, view the last page of the datatable.
My .xhtml page is:
<h:form id=...>
...
<p:dataTable var="webTemplate" id="templateTable" widgetVar="tbl1"/>
...
</h:form>
<h:form id=...>
...
<p:inputText id="txt_description" value="#{templateController.templateDescription}" label="templateDescription">
<f:validateLength for="txt_name" minimum="1"/>
<p:ajax event="change"
listener="#{calculatePageTable.setPageTableTemplate}" onsuccess="setTabIndexTT()"/>
</p:inputText>
...
</h:form>
<script type="text/javascript">
function setTabIndexTT(){
tbl1.getPaginator().setPage(#{calculatePageTable.pageTableTemplate});
}
</script>
bean:
#ManagedBean
#SessionScoped
public class CalculatePageTable {
private int pageTableTemplate = 0;
private int pageTableField = 0;
public void setPageTableTemplate() {
final DataTable d = (DataTable) FacesContext.getCurrentInstance().getViewRoot()
.findComponent("form:templateTable");
pageTableTemplate = d.getPageCount() - 1;
}
public int getPageTableTemplate() {
return pageTableTemplate;
}
public void setPageTemplateField() {
final DataTable d = (DataTable) FacesContext.getCurrentInstance().getViewRoot()
.findComponent("detailsTable:webTemplateUpdateTable");
pageTableField = (d.getPageCount() - 1);
}
public int getPageTableField() {
return pageTableField;
}
}
But the js function setTabIndexTT() is never called by onsuccess ajax...
How do I can set last page of my datatable when adding a row?
Primefaces version is 3.1.1
In your managed bean you can try this code:
public void setPageDataTable() {
final DataTable d = (DataTable) FacesContext.getCurrentInstance().getViewRoot()
.findComponent("form:templateTable");
int first = 1;
if (d.getRowCount() % ROWS_DATATABLE == 0) {
first = (d.getRowCount() - ROWS_DATATABLE);
}
else
{
first = (d.getRowCount()/ROWS_DATATABLE)*ROWS_DATATABLE;
}
d.setFirst(first);
}
Call this method when you add a new row
if the datatable widgetVar is dataTableWidget then use this:
<script type="text/javascript">
$(document).ready(function () {
dataTableWidget.paginator.setPage(0);
});
</script>
I would do it without any JavaScript. Primefaces's datatable has a first attribute, which is the index of the first data to display.
<p:dataTable first="#{calculatePageTable.first}"/>
...
<p:commandButton value="Add a row" action="#{calculatePageTable.addRow}"/>
And your backing bean:
public class CalculatePageTable {
private int first = 1;
public int getFirst(){
return first;
}
public void addRow(){
// 1. your stuff for adding the row
...
// 2. switch to the row
first = getFirstRowOnLastPage();
}
private int getFirstRowOnLastPage(){
...
}
}
You could avoid the explicit ID in your code by using bindings:
xhtml:
<p:dataTable var="webTemplate" id="templateTable" widgetVar="tbl1" binding="#{calculatePageTable.dataTable" />
bean:
public class CalculatePageTable {
private DataTable dataTable;
public DataTable getDataTable() {
return dataTable;
}
public void setDataTable(DataTable dataTable) {
this.dataTable = dataTable;
}
/* See Teg's answer */
public void setPageDataTable() {
int first = 1;
if (dataTable.getRowCount() % ROWS_DATATABLE == 0) {
first = (dataTable.getRowCount() - ROWS_DATATABLE);
}
else
{
first = (dataTable.getRowCount()/ROWS_DATATABLE)*ROWS_DATATABLE;
}
dataTable.setFirst(first);
}
}