Weird dataTable behaviour when combined with accordionPanel - primefaces

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!

Related

How to show a dynamic image in PrimeFaces 6.1 [duplicate]

I am using PrimeFaces 5.3 <p:fileUpload> to upload a PNG image and I would like to show a preview of it in <p:graphicImage> before saving in database.
Here's a MCVE:
<h:form enctype="multipart/form-data">
<p:fileUpload value="#{bean.uploadedFile}" mode="simple" />
<p:graphicImage value="#{bean.image}" />
<p:commandButton action="#{bean.preview}" ajax="false" value="Preview" />
</h:form>
private UploadedFile uploadedFile;
public UploadedFile getUploadedFile() {
return uploadedFile;
}
public void setUploadedFile(UploadedFile uploadedFile) {
this.uploadedFile = uploadedFile;
}
public void preview() {
// NOOP for now.
}
public StreamedContent getImage() {
if (uploadedFile == null) {
return new DefaultStreamedContent();
} else {
return new DefaultStreamedContent(new ByteArrayInputStream(uploadedFile.getContents()), "image/png");
}
}
No error occurring on the backing bean, and the image won't be load and display at front-end. The client mentions that the image returned a 404 not found error.
Your problem is two-fold. It failed because the uploaded file contents is request scoped and because the image is requested in a different HTTP request. To better understand the inner working, carefully read the answers on following closely related Q&A:
Display dynamic image from database with p:graphicImage and StreamedContent
How to choose the right bean scope?
To solve the first problem, you need to read the uploaded file contents immediately in the action method associated with the form submit. In your specific case, that would look like:
private UploadedFile uploadedFile;
private byte[] fileContents;
public void preview() {
fileContents = uploadedFile.getContents();
}
// ...
To solve the second problem, your best bet is to use the data URI scheme. This makes it possible to render the image directly in the very same response and therefore you can safely use a #ViewScoped bean without facing "context not active" issues or saving the byte[] in session or disk in order to enable serving the image in a different request. Browser support on data URI scheme is currently pretty good. Replace the entire <p:graphicImage> with below:
<ui:fragment rendered="#{not empty bean.uploadedFile}">
<img src="data:image/png;base64,#{bean.imageContentsAsBase64}" />
</ui:fragment>
public String getImageContentsAsBase64() {
return Base64.getEncoder().encodeToString(imageContents);
}
Note: I assume that Java 8 is available to you as java.util.Base64 was only introduced in that version. In case you're using an older Java version, use DatatypeConverter.printBase64Binary(imageContents) instead.
In case you happen to use JSF utility library OmniFaces, you can also just use its <o:graphicImage> component instead which is on contrary to <p:graphicImage> capable of directly referencing a byte[] and InputStream bean property and rendering a data URI.
<o:graphicImage value="#{bean.imageContents}" dataURI="true" rendered="#{not empty bean.imageContents}">

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).

PrimeFaces 5.2 selectCheckboxMenu not support Map<String,String> style selectItems

I am trying to use
Map<String,String>
style selectItems in my
<p:selectCheckboxMenu>
but unfortunately, it not works.
Backing bean:
#Named(value = "roleBean")
#SessionScoped
public class RoleBean(){
private Map<String,String> roleMap;
private String[] selectRoles;
......
......
public void findRoleMap(){
if(roles.size()>0){
for(Role r:roles){
roleMap.put(r.getRoleId(),r.getRoleName());
}
logger.info("Role Map size: " + roleMap.size());
}
}
}
jsf as:
<p:selectCheckboxMenu id="roles" value="#{roleBean.selectRoles}" label="Select your roles" filter="true" filterMatchMode="startsWith" height="200" panelStyle="width:200px">
<f:selectItems value="#{roleBean.roleMap.entrySet}" var="entry" itemLabel="#{entry.value}" itemValue="#{entry.key}"/>
</p:selectCheckboxMenu>
When I access the page, I found there is 6 records were added into the map. but unfortunately, there is nothing shows up in the selectCheckboxMenu drop down list.
If I change the UIComponent to
<p:selectOneMenu>
from
<p:selectCheckboxMenu>
it shows up properly in dropdown list.
I tried to find out through google, I didn't find anyone else trying to use key/value pair in Map style as selectItems in selectCheckboxMenu. I was very confused.
My question is: Does selectCheckboxMenu support key/value pair mapping style or not, if it does. How To do it?
Please advise!!

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.