PickList lose value - primefaces

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

Related

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']}"

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!

Combobox created dynamically from database (JSF)

I wanted to create in JSF a combobox (selectOneMenu). I want to fill this combobox with all logins from one column from Database (SELECT logins FROM database).
Will be much gratefull from any help.
In your backing bean (YourBean in the example) you should have an array of Strings (or of objects with a getter method that returns the String you want). For instance lets assume you have the following code in your backing bean:
private ArrayList<String> logins; // read from DB
private String selectedLogin; // this will hold the selected value
// this method will be called by the JSF framework to get the list
public ArrayList<String> getLogins()
{
return logins;
}
public String getSelectedLogin()
{
return selectedLogin;
}
public String setSelectedLogin(String sl)
{
selectedLogin = sl;
}
In you Facelets page, assuming you are on JSF 2.x:
<h:selectOneMenu value="#{YourBean.selectedLogin}">
<f:selectItems value="#{YourBean.logins}" itemLabel="#{l}" itemValue="#{l}" var="l"/>
</h:selectOneMenu>
This will create a select menu with all the options in the array. Once the form is submitted the value will be set in the String value of your backing bean.

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.

Reset input fields of a form after successful submit

I have a form which creates a new Employee. My backing bean is #SessionScoped. When I create the first employee, everything went well. However, when I'm about to create the second employee, the form still displays the properties of the first employee in the input fields.
How do I reset them without changing the scope of the bean? The scope is mandatory for other purposes.
i use a managed Bean ( controller) where i have "Create employe"
public String createEmploye()
{
employe = new Employe();
employe.setId(this.id);
employe.setNom(this.nom);
employe.setPrenom(this.prenom);
employe.setNum_telephone(this.num_telephone);
employe.setAdresse(this.adresse);
employe.setNum_poste(this.num_poste);
employeBean.addEmploye(employe);
employe.setNom("");
return "ListEmployes.xhtml?faces-redirect=true";
// return ("ListEmployes.xhtml");
}
Recreate the Employee instance after saving it in the DB.
public void save() {
service.save(employee);
employee = new Employee(); // <--- Just add this line.
}
Unrelated to the concrete problem, I however strongly recommend to reconsider your bean design. Shouldn't it rather be split into two beans? One request/view scoped for the form itself and another session scoped one for the real session scoped data which get injected in the request/view scoped one. This way you can after the save just perform a redirect to the same view in order to start with a clean form (and have the additional benefit that the very same employee doesn't get duplicated in the DB when you refresh the page after submit).
See also:
How to choose the right bean scope?
Update as per the update, it seems that you're duplicating/flattening all properties of Employee in the backing bean instead of letting the form refer them directly. I strongly recommend to not duplicate/flatten the model properties into the controller.
#ManagedBean
#SessionScoped
public class Manager {
private Employee employee = new Employee();
#EJB
private EmployeeService service;
public void createEmployee() {
service.create(employee);
employee = new Employee();
}
public Employee getEmployee() {
return employee;
}
}
with
<h:inputText value="#{manager.employee.firstname}" />
<h:inputText value="#{manager.employee.lastname}" />
<h:inputText value="#{manager.employee.telephone}" />
<h:inputText value="#{manager.employee.street}" />
...
I could solve this problem by creating a method that makes me void the object and calling it inside the form on page jsf.
public void clear_objet() {
this.producto = null;
}
<h:form id="form">
#{bean.limpiar_objeto()}
</h:form>