Primefaces datatable filtered rows become blank after sort - primefaces

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.

Related

ManagedBean method is changing dataTable object

I have a <p:dataTable /> that lists "System Parameters" of a web page I'm working. One of that parameters is a mail server password, and it is encoded in Base64 in the DB. In the last column of the datatable, I have a button to edit that parameters, and when I click on it, a bootstrap modal is showed with the fields "name" and "value".
I have a ManagedBean with #ViewScoped scope, and a function that also recognizes if the parameter to edit is the mail server password, to have the value decoded. The method gets the object as an argument the datatable iteration object. My problem is that the first time I open the modal, the value of the object is correct, but in second time the object value changes and it generates an exception because the value is already decoded, and the value has a dot inside and it generates the error.
I don't understand why the datatable object value is changed when I never change it literally.
I'm using PrimeFaces, BootsFaces, JSF 2.2. This is my <p:dataTable />:
<p:dataTable value="#{parameterBean.listParameter}" var="p"
paginator="true" rows="10"
reflow="true" id="tblParameter"
emptyMessage="No data available.">
<!-- Some columns -->
<p:column headerText="Options">
<b:commandButton value="Edit" icon="edit" look="info"
ajax="true" update="parametroForm"
onclick="ajax:parameterBean.editParameter(p)"
oncomplete="$('#parameterModal').modal('show');" />
</p:dataTable>
And this is my bean method:
public void editParameter(TblParameter p) {
System.out.println("ENCODE: " + p.getVlrParameter());
this.param = new TblParameter(); //- This is an object in the bean to access it from modal form.
try {
setParam(p);
if (getParam().getIdParameter().equals(new Long(4))) {//- Validate if is the server mail password.
byte[] clv64 = Base64.getDecoder().decode(p.getVlrParameter().getBytes());
getParam().setVlrParameter(new String(clv64));
}
} catch(Exception e) {
System.out.println("Error: " + e.getMessage());
e.printStackTrace();
}
}
Finally, as I said when I open for first time the modal, the output printed is this:
ENCODE: MTIzLjQ1Ng==
But in second time, it shows:
ENCODE: 123.456
java.lang.IllegalArgumentException: Illegal base64 character 2e
at java.util.Base64$Decoder.decode0(Base64.java:714)
at java.util.Base64$Decoder.decode(Base64.java:526)
at com.abcpagos.otis.beans.admin.ParametroBean.editarParametro(ParametroBean.java:146)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)...
If someone could please give me an explanation of this behavior, I would appreciate it very much.
Actually, you do change the parameter accidentially. Have a look at these lines:
byte[] clv64 = Base64.getDecoder().decode(p.getVlrParameter().getBytes());
// ^^^^^^^^^^^^^^^
getParam().setVlrParameter(new String(clv64));
// ^^^^^^^^^^^^^^^
So the second time you try to decode the parameter that's already stored in plain text.

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 3.5 multisort failure on pagination .. do we have fix?

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.