Primefaces 3.5 multisort failure on pagination .. do we have fix? - primefaces

Primefaces 3.5 introduced multisort on dataTable. But it has a bug with pagination.
Everytime paginator button is pressed, the sort column(sortMeta obj of the col) set at the time of table initial render is sent to the load method and not the selected sort column(s). If the initial sortOrder is not set, a null is sent.
Has there been a fix to this problem? Does anybody know if this has been fixed in any of the Elite versions? Or if there is a workaround for it?
Need some urgent help.
Thanks

I was able to overcome the problem by doing some additional coding in the application. This wouldn't be needed if the original bug is fixed in the later versions.
My solution
I have added 2 ajax events, for 'sort' and 'page' on the table. I saw that on each 'sort' the table had the right list of multisortmeta, but on the pagination call, it was getting over written with initial data.
So I have saved the list of multisortmeta in the bean on each sort call and on pagination call i have over written the table's list of multisortmeta with what i have in the bean. My bean is a session bean hence there is no problem with saving the list.
Once this is done the multisort is working on pagination. Below is my code
view/xhtml code
<p:dataTable id="userDataTable"
value="#{userBean.userModel}" var="usr"
paginator="true" paginatorAlwaysVisible="false" sortMode="multiple"
rowsPerPageTemplate="20,40,60" rows="20"
sortBy="#{userBean.preSortOrder}" lazy="true"
resizableColumns="false" >
<p:ajax event="page" listener="#{userBean.onPage}" ></p:ajax>
<p:ajax event="sort" listener="#{userBean.onSort}" ></p:ajax>
Bean code
public void onSort( SortEvent event) {
UIViewRoot viewRoot = FacesContext.getCurrentInstance().getViewRoot();
UIComponent tableComp = viewRoot.findComponent("userForm:userDataTable");
DataTable table = ((DataTable)tableComp);
preSortOrder = table.getMultiSortMeta();
}
public void onPage() {
UIViewRoot viewRoot = FacesContext.getCurrentInstance().getViewRoot();
UIComponent tableComp = viewRoot.findComponent("userForm:userDataTable");
DataTable table = ((DataTable)tableComp);
table.setMultiSortMeta(preSortOrder);
}
Hope this will be useful to others as well.

Related

Primefaces datatable filtered rows become blank after sort

My Primefaces table is non-lazy loaded, displays POJOs and support default filtering and sorting.
Filtering works fine alone, sorting works fine alone. But when I type something in any column's filter, and then click a sort key (on any column), all the filtered objects become blank (the number of rows is the correct filtered rows). If I erase the last character in the filter (thus triggering filter), all the filtered rows re-appear OK and the sort order is honored. Here is my datatable:
<p:dataTable id="assets" rows="10" paginator="true" filteredValue="#{assetBean.filteredAssets}"
value="#{assetBean.assetGroups}" var="asset">
<p:column headerText="Location" filterBy="#{asset.installation}" field="installation">
</p:column>
<p:column headerText="Name" filterBy="#{asset.name}" field="name">
</p:column>
<p:column headerText="Type" filterBy="#{asset.udt_id}" field="udt_id">
</p:column>
</p:dataTable>
I tried debugging with logging in the setter and getter of filterValue like this in AssetBean.java:
#javax.inject.Named
#javax.faces.view.ViewScoped
public class AssetBean implements java.io.Serializable {
private List<Asset> filteredAssets;
public List<Asset> getFilteredAssets() {
Logger.getLogger(AssetBean.class.getName()).log(Level.INFO, "getFilteredAssets {0}", filteredAssets.size());
return filteredAssets;
}
public void setFilteredAssets(List<Asset> filteredAssets) {
this.filteredAssets = filteredAssets;
Logger.getLogger(AssetBean.class.getName()).log(Level.INFO, "setFilteredAssets {0}", this.filteredAssets.size());
if (filteredAssets.size()>0) {
Asset a = this.filteredAssets.get(0);
Logger.getLogger(AssetBean.class.getName()).log(Level.INFO, "first name {0}", a.getName());
}
}
....
These are interesting things noted:
getFilteredAssets is never called
setFilteredAssets is first called as expected. When a sortKey is pressed, setFilteredAssets is found to be passed a List of the correct size, but the Asset.name is null. When the filter is changed, setFilteredAssets is found to be passed a List of the correct size, and correct contents so the datatable shows correct data again.
Debugging in the browser shows that the ajax response indeed contains empty rows. For the problematic sort, the POST body of the ajax request contains:
{javax.faces.partial.ajax: "true", ...
main-form:assets_sorting: "true", main-form:assets_skipChildren: "true",
main-form:assets_sortKey: "main-form:assets:j_idt16",
main-form:assets_sortDir: "-1",
main-form:assets:j_idt14filter: "",
main-form:assets:j_idt15filter: "Pla",
main-form:assets:j_idt16filter: "",
}
When I changed the filter, the POST body of the ajax request is the same as above, except main-form:assets:j_idt15filter contains the new value and main-form:assets_sorting, sortKey, sortDir are missing.
My environment TomEE (javaee-web-api 7.0), Primefaces 10.0.0, Omnifaces 3.13, OpenJDK 11.
I solved by a workaround by wrapping a placeholder ajax listener for the sort event to trigger a filter request:
<p:ajax event="sort" listener="#{assetBean.sortPlaceholder}" oncomplete="PF('data-table1').filter()"/>
This seems to be a bug in Primefaces.

PropertyNotFoundException for p:column filterValue attribute

we are trying to migrate our application from tomcat/websphere to was liberty profile.
Additionally we are upgrading the myfaces-version, we are using 2.1, to myfaces-2.2.
To save the current state of a table (filtering) we store the filtered value in a map and read it when loading the table (filterValue attribute of p:column).
When initially loading the table the correct method will be used (in our case its getFilterValue in the DataModel). But if we start filtering a column, the method wont be found anymore and the following exception occurs:
javax.el.PropertyNotFoundException: Die Eigenschaft 'getFilterValue' wurde nicht im Typ package.LazyModel gefunden.
javax.el.BeanELResolver$BeanProperties.get(BeanELResolver.java:245)
javax.el.BeanELResolver$BeanProperties.access$300(BeanELResolver.java:222)
javax.el.BeanELResolver.property(BeanELResolver.java:332)
javax.el.BeanELResolver.getType(BeanELResolver.java:83)
javax.el.CompositeELResolver.getType(CompositeELResolver.java:99)
org.apache.myfaces.el.unified.resolver.FacesCompositeELResolver.getType(FacesCompositeELResolver.java:150)
org.apache.el.parser.AstValue.setValue(AstValue.java:199)
org.apache.el.ValueExpressionImpl.setValue(ValueExpressionImpl.java:257)
org.jboss.weld.el.WeldValueExpression.setValue(WeldValueExpression.java:64)
org.apache.myfaces.view.facelets.el.ContextAwareTagValueExpression.setValue(ContextAwareTagValueExpression.java:153)
org.primefaces.component.datatable.DataTable.processUpdates(DataTable.java:746)
org.apache.myfaces.context.servlet.PartialViewContextImpl$PhaseAwareVisitCallback.visit(PartialViewContextImpl.java:787)
org.apache.myfaces.component.visit.PartialVisitContext.invokeVisitCallback(PartialVisitContext.java:213)
org.primefaces.component.api.UIData.visitTree(UIData.java:822)
The table:
<p:dataTable id="table" var="group"
value="#{bean.lazyModel}"
selection="#{bean.selectedMulti}"
rows="#{bean.lazyModel.rows}" paginator="true"
currentPageReportTemplate="#{msg['data.table.pagereport']}"
paginatorTemplate="#{msg['data.table.paginator']}"
rowsPerPageTemplate="#{msg['data.table.rows']}"
resizableColumns="true" rowKey="#{group.pkId}" lazy="true"
filterDelay="1000" emptyMessage="#{msg['data.table.empty']}"
style="font-size: 8pt;"
tableStyle="font-size: 8pt; table-layout:auto;"
first="#{bean.lazyModel.first_m}"> >
<p:column headerText="Name"
sortBy="#{group.name}" filterBy="#{group.name}"
filterValue="#{bean.lazyModel.getFilterValue('name')}"
filterStyleClass="column_filter" styleClass="wrap">
<h:outputText value="#{group.name}" />
</p:column>
...
The lazymodel:
#Named
#Scope(value = "prototype")
public class LazyModel extends AbstractLazyModel<Brand> {
private static final long serialVersionUID = 2247660292777600670L;
/**
* Konstruktor
*/
public LazyModel() {
super();
}
public Object getFilterValue(final String keyForColumn) {
return this.filterManager.getFilterField(this.getKeyForPage(), keyForColumn);
}
I think this should be the most important things to know.
So, i dont understand what changed between these versions that trigger the exception.
Every help would be great. TIA!
I don't know why this has worked before (it should not have (properly)), but the error you are currently getting is sort of expected. The filterValue attribute should be bound to a property with read and write access. You could bind each column filter value to an individual property, but it is more convenient to use a Map<String,Object> for your filter values.
Bean (lazy model in your case):
private Map<String,Object> filterValues = new HashMap<>();
// ... add getter and setter for filterValues
XHTML:
filterValue="#{bean.lazyModel.filterValues['name']}"

PickList lose value

I have a simple persistent object (Hibernate, if it makes any different):
#Entity
Class ObjectA
{
private Long id;
private String name;
private String description;
private List<ObjectA> children = new LinkedList<>();
...
...
...
public org.primefaces.model.DualListModel<ObjectA> getDualList() {
org.primefaces.model.DualListModel<ObjectA> l = new org.primefaces.model.DualListModel<>();
l.setSource(this.children);
l.setTarget(database.getAllChildren()); // performs database query to retrieve all objectA that are not corrently linked to any objectA.
...
return l;
}
public setDualList(org.primefaces.model.DualListModel<ObjectA> l) {
this.children = l.getSource();
...
}
}
The PrimeFaces code looks like (relevant snippet):
...
<p:dataTable id="table" value="#{managedBeanA.getAllObjects}" var="iterator" paginator="true" rows="10" >
...
...
<p:rowExpansion>
<p:pickList value="#{iterator.dualList}" var="l" itemLabel="#{l.name}" itemValue="#{requirement.id}">
<f:facet name="sourceCaption">Linked Children</f:facet>
<f:facet name="targetCaption">Unlinked Children</f:facet>
<p:ajax event="transfer" listener="#{managedBeanA.handleTransfer(iterator)}"/>
</p:pickList>
</p:rowExpansion>
</p:dataTable>
What managedBeanA.handleTransfer does is simply persist the passed object.
Everything seems to work lovely, I can expand the row and getDualList is called. When I expand another row, another getDualList is called - all as expected.
When I move items from source to target, managedBeanA.handleTransfer is called and the relevant object is persisted in the database.
HOWEVER, and here is the question, when the table is updated - either form submit or ajax, I can see that setDualList is called for EVERY item in the p:dataTable with EMPTY getSource which, in essence, break all the links previously persisted (i.e. this.children = null). The previously linked ObjectA objects are still in database but they are no longer linked...
Any ideas what is going on?
I have found a solution to the problem. It is probably only a workaround as I am not sure what the problem actually is - in the setDualList method, I have added the following line:
if (l.getSource().isEmpty() && l.getTarget().isEmpty()) return;
which solves all my problems (as the symptom of the issues is that the setter is called with no values).

Weird dataTable behaviour when combined with accordionPanel

I get a very strange behaviour when I combine the accordionPanel (dynamic tabs) together with a dataTable. In my project the total amount of dynamic tabs can get really big (>1000) so I really need the dynamic loading of the accordionPanel working, otherwise it is way too slow.
The problem
Instead of only loading the datatable for the currently selected tab, the getter method to load the elements from a list variable is being called 6 times the total amount of dynamic tabs. So if I have 300 dynamic tabs, the getter method is called 1800 times. If I however do not use a dataTable but i.e. just output text, the getter method is only called once like it should be.
Here is a small working example which produces the unwanted behaviour:
xhtml
<p:accordionPanel id="datasetHistoryAccordionTab" value="#{Bean.orderDatasets}" var="dataset" dynamic="true">
<p:tab title="Dataset ##{dataset.id}">
<p:tabView>
<p:tab title="All Processes">
<p:dataTable value="#{Bean.datasetProgressHistoryAll}" />
<!-- <h:outputText value="#{Bean.datasetProgressHistoryAll}" /> -->
</p:tab>
</p:tabView>
</p:tab>
</p:accordionPanel>
Bean
#ManagedBean
#ViewScoped
public class Bean implements Serializable
{
private List<DatasetBE> orderDatasets = new ArrayList<DatasetBE>(300);
private List<DatasetHistoryBE> datasetHistoryAll = new ArrayList<DatasetHistoryBE>();
public List<DatasetBE> getOrderDatasets() {
return orderDatasets;
}
public void setOrderDatasets(List<DatasetBE> orderDatasets) {
this.orderDatasets = orderDatasets;
}
public List<DatasetHistoryBE> getDatasetHistoryAll() {
log.debug("getDatasetHistoryAll() called...");
return datasetHistoryAll;
}
public void setDatasetProgressHistoryAll(List<DatasetHistoryBE> datasetHistoryAll) {
this.datasetHistoryAll = datasetHistoryAll;
}
}
The initial state is OK but as soon as I change to another tab the getter method is being called like crazy...
Is this a bug in PrimeFaces or am I doing something wrong? In my project I still use PF 3.5 but for testing purposes I also tried PF 4.0 but the same strange behaviour applies... Any help would be greatly appreciated!

Primefaces <p:collector> adding only one row

I am trying to use p:collector to collect list of state objects. The problem is that only one row gets added to the stateList. After adding one row, when I click the commandButton, nothing gets added to the stateList. Here is my code. It seems I am missing out something obvious.
JSF page
Code:
>" action="#{bulkStateBean.reInit}" update="f:statePanel" >
Backing Bean (bulkStateBean)
Code:
private List stateList = new ArrayList();
private State state = new State();
public String reInit() {
state = new State();
return null;
}
Change scope of the bean to "session" in Faces-config.xml-> Managed Beans.
I finally solved it. The entity class State has the #override methods, which were generated while creating new entity through create new entity wizard. remove all the overridden methods from the entity and save it. Thats all. Now it will work. It worked for me.