I'm having some trouble when trying to select single rows in a datatable (I can't figure out what's going on, because my code is based on the primefaces showcase).
The table is filled first with result matches, then I'm trying to select a row, and finally trying to do some process. The problem appears when I try to process that single row, none of the actual methods its being called.
<h:form id="form2">
<p:dialog header="Agregar Producto" widgetVar="dlgAgregarProducto" modal="true" width="800" height="300">
<h:outputText value="Código"/>
<h:inputText value="#{promocionController.strIdProducto}"/>
<h:outputText value="Nombre"/>
<h:inputText value="#{promocionController.strNombre}"/>
<p:commandButton id="btnBuscarProducto" value="Buscar" action="#{promocionController.buscarProductosIdNombre}" update="tableBusqueda"/>
<p:commandButton id="btnAgregarProducto" value="Agregar" action="#{promocionController.agregarProductos}" />
<p:dataTable id="tableBusqueda" var="prod" value="#{promocionController.obtenerProductos()}"
rowKey="#{prod.idProducto}" selection="#{promocionController.arrProductosSeleccionados}" selectionMode="single">
<p:column style="width:24%">
<f:facet name="header">
<h:outputText value="Código"/>
</f:facet>
<h:outputText value="#{prod.idProducto}"/>
</p:column>
<p:column style="width:24%">
<f:facet name="header">
<h:outputText value="Foto"/>
</f:facet>
<h:outputText value="foto"/>
</p:column>
<p:column style="width:24%">
<f:facet name="header">
<h:outputText value="Descripción"/>
</f:facet>
<h:outputText value="#{prod.descripcion}"/>
</p:column>
<p:column style="width:24%">
<f:facet name="header">
<h:outputText value="Marca"/>
</f:facet>
<h:outputText value=""/>
</p:column>
<p:column style="width:24%">
<f:facet name="header">
<h:outputText value="Modelo"/>
</f:facet>
<h:outputText value="#{prod.modelo}"/>
</p:column>
<p:column style="width:24%">
<f:facet name="header">
<h:outputText value="Precio"/>
</f:facet>
<h:outputText value="#{prod.precio}"/>
</p:column>
</p:dataTable>
</p:dialog>
</h:form>
Backing bean:
#ManagedBean(name = "promocionController")
#SessionScoped
public class PromocionCO {
arrProductosPromocion = new ArrayList<Producto>();
arrProductosSeleccionados = new ArrayList<Producto>();
}
public void buscarProductosIdNombre() {
productoDAO = new ProductoDAO();
arrProductosBusqueda = new ArrayList<Producto>();
arrProductosSeleccionados = new ArrayList<Producto>();
// si la búsqueda es por nombre
if (strIdProducto.compareTo("") == 0) {
arrProductosBusqueda = productoDAO.obtenerProductosPorNombre(strNombre);
} // si la búsqueda es por id
else {
producto = productoDAO.obtenerProductoPorId(strIdProducto);
if (producto != null) {
arrProductosBusqueda.add(producto);
} else {
arrProductosBusqueda = null;
}
}
}
public void agregarProductos() {
for (int i = 0; i < arrProductosSeleccionados.size(); i++) {
arrProductosPromocion.add(arrProductosSeleccionados.get(i));
}
}
public ArrayList<Producto> obtenerProductos() {
return arrProductosBusqueda;
}
public ArrayList<Producto> getArrProductosSeleccionados() {
return arrProductosSeleccionados;
}
public void setArrProductosSeleccionados(ArrayList<Producto> arrProductosSeleccionados) {
if (arrProductosSeleccionados != null) {
this.arrProductosSeleccionados = arrProductosSeleccionados;
}
}
This is because your selection attribute points to an ArrayList of Producto instead of a single element. Since you are using datatable with selectionMode="single" the selected attribute should point a single element in your backing bean:
Producto selectedProducto;
public Producto getSelectedProducto() {
return selectedProducto;
}
public void setSelectedProducto(Producto producto) {
selectedProducto = producto;
}
Finally, specify this element in your dataTable selection:
<p:dataTable id="tableBusqueda" var="prod" selectionMode="single"
value="#{promocionController.obtenerProductos}"
selection="#{promocionController.selectedProducto}">
Now after selecting a row the public void setSelectedProducto(Producto producto) method will be called.
Unrelated to the concrete problem, it is more preferable to use getter/setter methods when pointing to a value in your backing bean:
<p:dataTable id="tableBusqueda" var="prod"
value="#{promocionController.obtenerProductos}" .../>
In general, an appropriate getter/setter should looks like get<NameOfTheObject>, in your case:
public List<Producto> getObtenerProductos()
Related
I've a dataTable in my JSF page where I list all my object. Usually I use the filterBy and filterMatchMode to perform this type of operation, but in this case I have to filter the list for a given column, for a range of value. How Can I achieve this?
<p:dataTable id="dtOrders" var="productionOrder"
value="#{productionOrdersView.orders}" selectionMode="single"
reflow="true" selection="#{productionOrdersView.selected}"
filteredValue="#{productionOrdersView.filtered}"
widgetVar="ordersTable" rowKey="#{productionOrder.pk}"
paginator="true" rows="20">
<f:facet name="header">
Bolle di lavorazione
</f:facet>
<p:column headerText="Numero bolla"
sortBy="#{productionOrder.orderNumber}"
filterFunction="#{productionOrdersView.filter()}"
filterOptions="contains">
<f:facet name="filter">
<h:inputHidden id="filter" />
</f:facet>
<f:facet name="header">
<h:outputText value="Numero bolla" />
<h:outputText value="Dalla" />
<p:inputText id="from"
value="#{productionOrdersView.orderNumberStartFilter}">
<p:ajax event="keyup" />
</p:inputText>
<h:outputText value="alla" />
<p:inputText id="to"
value="#{productionOrdersView.orderNumberEndFilter}">
<p:ajax event="change"
listener="#{productionOrdersView.filter()}" update="dtOrders"
oncomplete="PF('dtOrders').filter()" />
</p:inputText>
</f:facet>
<h:outputText value="#{productionOrder.orderNumber}" />
</p:column>
And the bean method:
public void filter() {
System.out.println("Filtering...");
System.out.println(orderNumberStartFilter+" "+orderNumberEndFilter);
if(orderNumberStartFilter!=null && orderNumberEndFilter!=null) {
this.filtered = productionOrderController.findFromToOrderNumber(Integer.valueOf(orderNumberStartFilter), Integer.valueOf(orderNumberEndFilter));
}else {
if(orderNumberStartFilter!=null) {
this.filtered = productionOrderController.findFromToOrderNumber(Integer.valueOf(orderNumberStartFilter), Integer.valueOf(orderNumberStartFilter));
return;
}
if(orderNumberEndFilter!=null) {
this.filtered = productionOrderController.findFromToOrderNumber(Integer.valueOf(orderNumberEndFilter), Integer.valueOf(orderNumberEndFilter));
return;
}
this.filtered = orders;
}
System.out.println(filtered.size());
}
How Can I filter the list? Is this a good approach to perform this operation? Also I want that if just one value is inserted, the filter operation is made equals (so exact that order). I've search on the web and I've seen a lot of example for date, but nothing for integer values.
Update
list.xhtml:72 Uncaught TypeError: Cannot read property 'filter' of undefined
at Object.onco (list.xhtml:72)
at Object.<anonymous> (core.js.xhtml?ln=primefaces&v=7.0:3)
at u (jquery.js.xhtml?ln=primefaces&v=7.0:2)
at Object.fireWith [as resolveWith] (jquery.js.xhtml?ln=primefaces&v=7.0:2)
at k (jquery.js.xhtml?ln=primefaces&v=7.0:2)
at XMLHttpRequest.<anonymous> (jquery.js.xhtml?ln=primefaces&v=7.0:2)
onco # list.xhtml:72
(anonymous) # core.js.xhtml?ln=primefaces&v=7.0:3
u # jquery.js.xhtml?ln=primefaces&v=7.0:2
fireWith # jquery.js.xhtml?ln=primefaces&v=7.0:2
k # jquery.js.xhtml?ln=primefaces&v=7.0:2
(anonymous) # jquery.js.xhtml?ln=primefaces&v=7.0:2
Make your code like that
The xhtml
<p:column headerText="Order number" sortBy="#{productionOrder.orderNumber}"
filterFunction="#{productionOrdersView.filter()}"
filterMatchMode="contains">
<f:facet name="filter">
<h:inputHidden id="filter"/>
</f:facet>
<f:facet name="header">
<p:inputText id="from" value="#{productionOrdersView.from}">
<p:ajax event="keyup" />
</p:inputText>
<h:outputText class="fa fa-arrows-h fa-2x" style="vertical-align: middle;"/>
<p:inputText id="to" value="#{productionOrdersView.to}">
<p:ajax event="change"
listener="#{productionOrdersView.filter()}" update="your_dataTable_Id"
oncomplete="PF('your_dataTable_widgetVar').filter()"/>
</p:inputText>
</f:facet>
<h:outputText value="#{productionOrdersView.orderNumber}" />
</p:column>
In the bean make 2 Integer variables from to with getter and setter and make o method to filter your data
private Integer from;
private Integer to;
..............
//Method that filter the data
public void filter(){
//Filter your data between from - to
.......................
}
}
You can improve this code but I think it is a good base.
UPDATE ANSWER
The problem with your code is that you don't put the filtered list as a value in your datatable.
To work this code You can do 2 things. (Do one of them)
1) Change the filtered function as below.
public void filter() {
System.out.println("Filtering...");
System.out.println(orderNumberStartFilter+" "+orderNumberEndFilter);
if(orderNumberStartFilter!=null && orderNumberEndFilter!=null) {
this.filtered = productionOrderController.findFromToOrderNumber(Integer.valueOf(orderNumberStartFilter), Integer.valueOf(orderNumberEndFilter));
}else {
if(orderNumberStartFilter!=null) {
orders = productionOrderController.findFromToOrderNumber(Integer.valueOf(orderNumberStartFilter), Integer.valueOf(orderNumberStartFilter));
return;
}
if(orderNumberEndFilter!=null) {
orders = productionOrderController.findFromToOrderNumber(Integer.valueOf(orderNumberEndFilter), Integer.valueOf(orderNumberEndFilter));
return;
}
}
System.out.println(orders.size());
}
2) Change the variable in datatable value code.
<p:dataTable id="dtOrders" var="productionOrder"
value="#{productionOrdersView.filtered}" selectionMode="single"
reflow="true" selection="#{productionOrdersView.selected}"
widgetVar="ordersTable" rowKey="#{productionOrder.pk}"
paginator="true" rows="20">
.................
I'm working on a theme for an app I'm making with PrimeFaces 6.2 (community edition) and I'd like to get my simulated DAO objects working before I proceed with my css templating.
I've got an issue I came across in the past and I can't find the right answer for it again. Would someone point out an error I've made somewhere in my code?
Details:
I've made a somewhat complex DataTable using PrimeFaces LazyDataModel with little help from PrimeFaces Showcase pages. My main issue is, when I write something in the filter fields or click on any column headers to do data sorting or even click on pagination buttons I get an unexpexted data rendering issue.
Filtered, sorted and paginated results get displayed in a single concatenated row what I don't want. I've posted images and code further below for insight.
Also, I'd like to point out:
No exceptions in JS console.
No exceptions in Java console.
don't mind the missing pagination icons (text-indent: 0;)
XHTML:
<h:form id="input-form-dt2">
<H4>DATA TABLE - LAZY MODEL</H4>
<div class="flex-container">
<p:outputPanel id="dev-input-panel-13">
<p:dataTable var="DOTable" value="#{dtModelLazy.DOTList}" paginator="true" rows="10" rowKey="#{DOTable.userID}"
paginatorTemplate="{RowsPerPageDropdown} {FirstPageLink} {PreviousPageLink} {CurrentPageReport} {NextPageLink} {LastPageLink}"
rowsPerPageTemplate="5,10,15,25,50" selectionMode="single" selection="#{dtModelLazy.selectedObj}" id="DTModelB" lazy="true">
<p:ajax event="rowSelect" listener="#{dtModelLazy.onRowSelect}" update="input-form-dt2:dlgDTOObjDetail" oncomplete="PF('DTObjDialog').show()" />
<p:column headerText="User ID" sortBy="#{DOTable.userID}" filterBy="#{DOTable.userID}" filterMatchMode="contains">
<h:outputText value="#{DOTable.userID}" />
</p:column>
<p:column headerText="Name" sortBy="#{DOTable.name}" filterBy="#{DOTable.name}" filterMatchMode="contains">
<h:outputText value="#{DOTable.name}" />
</p:column>
<p:column headerText="Surname" sortBy="#{DOTable.surname}" filterBy="#{DOTable.surname}" filterMatchMode="contains">
<h:outputText value="#{DOTable.surname}" />
</p:column>
<p:column headerText="Age" sortBy="#{DOTable.age}" filterBy="#{DOTable.age}" filterMatchMode="contains">
<h:outputText value="#{DOTable.age}" />
</p:column>
<p:column headerText="Address" sortBy="#{DOTable.address}" filterBy="#{DOTable.address}" filterMatchMode="contains">
<h:outputText value="#{DOTable.address}" />
</p:column>
<p:column headerText="City" sortBy="#{DOTable.city}" filterBy="#{DOTable.city}" filterMatchMode="contains">
<h:outputText value="#{DOTable.city}" />
</p:column>
<p:column headerText="Post code" sortBy="#{DOTable.postCode}" filterBy="#{DOTable.postCode}" filterMatchMode="contains">
<h:outputText value="#{DOTable.postCode}" />
</p:column>
<p:column headerText="Country code" sortBy="#{DOTable.countryCode}" filterBy="#{DOTable.countryCode}" filterMatchMode="contains">
<h:outputText value="#{DOTable.countryCode}" />
</p:column>
<p:column headerText="Phone number" sortBy="#{DOTable.phoneNumber}" filterBy="#{DOTable.phoneNumber}" filterMatchMode="contains">
<h:outputText value="#{DOTable.phoneNumber}" />
</p:column>
<p:column headerText="Avatar hash" sortBy="#{DOTable.photoID}" filterBy="#{DOTable.photoID}" filterMatchMode="contains">
<h:outputText value="#{DOTable.photoID}" />
</p:column>
</p:dataTable>
<p:dialog id="dlgDTOObjDetail" header="DataTable Object Detail" widgetVar="DTObjDialog" modal="true" showEffect="fade" hideEffect="fade" resizable="false">
<p:outputPanel id="DTObjDetail">
<p:panelGrid columns="2" rendered="#{not empty dtModelLazy.selectedObj}" columnClasses="label,value">
<h:outputText value="User ID: " />
<h:outputText value="#{dtModelLazy.selectedObj.userID}" />
<h:outputText value="Name: " />
<h:outputText value="#{dtModelLazy.selectedObj.name}" />
<h:outputText value="Surname: " />
<h:outputText value="#{dtModelLazy.selectedObj.surname}" />
<h:outputText value="Age: " />
<h:outputText value="#{dtModelLazy.selectedObj.age}" />
<h:outputText value="Address: " />
<h:outputText value="#{dtModelLazy.selectedObj.address}" />
<h:outputText value="City: " />
<h:outputText value="#{dtModelLazy.selectedObj.city}" />
<h:outputText value="Post code: " />
<h:outputText value="#{dtModelLazy.selectedObj.postCode}" />
<h:outputText value="Country code: " />
<h:outputText value="#{dtModelLazy.selectedObj.countryCode}" />
<h:outputText value="Phone number: " />
<h:outputText value="#{dtModelLazy.selectedObj.phoneNumber}" />
<h:outputText value="Photo hash: " />
<h:outputText value="#{dtModelLazy.selectedObj.photoID}" />
</p:panelGrid>
</p:outputPanel>
</p:dialog>
</p:outputPanel>
</div>
<hr></hr>
</h:form>
LAZY MODEL:
public class DataTableModelLazy extends LazyDataModel<DODataTable> {
private static final long serialVersionUID = -2647349397077805782L;
private List<DODataTable> datasource;
public DataTableModelLazy(List<DODataTable> datasource) {
this.datasource = datasource;
}
#Override
public DODataTable getRowData(String rowKey) {
for(DODataTable dtObj : datasource) {
if(dtObj.getUserID().equals(rowKey))
return dtObj;
}
return null;
}
#Override
public Object getRowKey(DODataTable dtObj) {
return dtObj.getUserID();
}
#Override
public List<DODataTable> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String,Object> filters) {
List<DODataTable> data = new ArrayList<DODataTable>();
//filter
for(DODataTable dtObj : datasource) {
boolean match = true;
if(filters != null) {
for (Iterator<String> it = filters.keySet().iterator(); it.hasNext();) {
try {
String filterProperty = it.next();
Object filterValue = filters.get(filterProperty);
Field field = dtObj.getClass().getDeclaredField(filterProperty);
field.setAccessible(true);
String fieldValue = String.valueOf(field.get(dtObj));
field.setAccessible(false);
if(filterValue == null || fieldValue.startsWith(filterValue.toString())) {
match = true;
} else {
match = false;
break;
}
} catch(Exception e) {
match = false;
}
}
}
if(match) {
data.add(dtObj);
}
}
//sort
if(sortField != null) {
Collections.sort(data, new DataTableModelLazySorter(sortField, sortOrder));
}
//rowCount
int dataSize = data.size();
this.setRowCount(dataSize);
//paginate
if(dataSize > pageSize) {
try {
return data.subList(first, first + pageSize);
} catch(IndexOutOfBoundsException e) {
return data.subList(first, first + (dataSize % pageSize));
}
} else {
return data;
}
}
}
VIEW BEAN:
#Named("dtModelLazy")
#ViewScoped
public class DataGeneratorBeanLazy implements Serializable {
private static final long serialVersionUID = -5918527333909822277L;
private LazyDataModel<DODataTable> DOTList;
private DODataTable selectedObj;
#Inject
private DataGeneratorBean dataGen;
#PostConstruct
public void init() {
DOTList = new DataTableModelLazy(dataGen.createDTObjects(1500));
}
public LazyDataModel<DODataTable> getDOTList() {
return DOTList;
}
public void setDOTList(LazyDataModel<DODataTable> dOTList) {
DOTList = dOTList;
}
public void onRowSelect(SelectEvent event) {
FacesMessage msg = new FacesMessage("DataTable object selected!", ((DODataTable) event.getObject()).getUserID());
FacesContext.getCurrentInstance().addMessage(null, msg);
}
public DODataTable getSelectedObj() {
return selectedObj;
}
public void setSelectedObj(DODataTable selectedObj) {
this.selectedObj = selectedObj;
}
}
Update 1
I have modified the update property as update="input-form-dt2:dlgDTOObjDetail"to meet the suggestion provided. Also, I added the id property for the dialog. The issue still remains.
Update 2
I've changed my approach and started with the basic DataTable first. Also, I've stripped the .xhtml to a bare minimum. It contains only a form with the DataTable inside like so:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:p="http://primefaces.org/ui">
<div>
UI components design
</div>
<h:form id="input-form-dt1">
<h4>DATA TABLE - BASIC</h4>
<p:dataTable id="DTableA" var="dataObject" value="#{dataTableBean.objectList}" paginator="true" rows="10" rowKey="#{dataObject.id}"
paginatorTemplate="{RowsPerPageDropdown} {FirstPageLink} {PreviousPageLink} {CurrentPageReport} {NextPageLink} {LastPageLink}"
rowsPerPageTemplate="5,10,15,25,50">
<p:column headerText="User ID" sortBy="#{dataObject.userID}" filterBy="#{dataObject.userID}" filterMatchMode="contains">
<h:outputText value="#{dataObject.userID}" />
</p:column>
<p:column headerText="Name" sortBy="#{dataObject.name}" filterBy="#{dataObject.name}" filterMatchMode="contains" >
<h:outputText value="#{dataObject.name}" />
</p:column>
<p:column headerText="Surname" sortBy="#{dataObject.surname}" filterBy="#{dataObject.surname}" filterMatchMode="contains" >
<h:outputText value="#{dataObject.surname}" />
</p:column>
<p:column headerText="Age" sortBy="#{dataObject.age}" filterBy="#{dataObject.age}" filterMatchMode="contains" >
<h:outputText value="#{dataObject.age}" />
</p:column>
<p:column headerText="Address" sortBy="#{dataObject.address}" filterBy="#{dataObject.address}" filterMatchMode="contains" >
<h:outputText value="#{dataObject.address}" />
</p:column>
<p:column headerText="City" sortBy="#{dataObject.city}" filterBy="#{dataObject.city}" filterMatchMode="contains" >
<h:outputText value="#{dataObject.city}" />
</p:column>
<p:column headerText="Post code" sortBy="#{dataObject.postCode}" filterBy="#{dataObject.postCode}" filterMatchMode="contains" >
<h:outputText value="#{DOTable.postCode}" />
</p:column>
<p:column headerText="Country code" sortBy="#{dataObject.countryCode}" filterBy="#{dataObject.countryCode}" filterMatchMode="contains" >
<h:outputText value="#{dataObject.countryCode}" />
</p:column>
<p:column headerText="Phone number" sortBy="#{dataObject.phoneNumber}" filterBy="#{dataObject.phoneNumber}" filterMatchMode="contains" >
<h:outputText value="#{dataObject.phoneNumber}" />
</p:column>
<p:column headerText="Avatar hash" sortBy="#{dataObject.photoID}" filterBy="#{dataObject.photoID}" filterMatchMode="contains">
<h:outputText value="#{dataObject.photoID}" />
</p:column>
</p:dataTable>
</h:form>
</ui:composition>
As you can see I've also removed all event listeners. I've added a new field to my data object (id of type Integer) and bound DataTables rowKey to it (previously bound to userID of type String - not a good idea). My DataTable backing bean is now as basic as it can be:
#Named("dataTableBean")
#ViewScoped
public class DataTableBean implements Serializable {
private static final long serialVersionUID = -1662465661106141910L;
private List<DTObject> objectList;
#Inject
private DataGeneratorBean dataGen;
#PostConstruct
public void init() {
setObjectList(dataGen.createDTObjects(1500));
}
public List<DTObject> getObjectList() {
if (objectList == null) {
return new ArrayList<>();
} else {
return objectList;
}
}
public void setObjectList(List<DTObject> objectList) {
this.objectList = objectList;
}
}
Now, there are no custom filters, sorters or paginators of any type, not even a data model, just raw data objects in a simple list. The output result is exactly the same as before when paginator buttons are clicked or data gets filtered. All resulting data still gets displayed in a single concatenated line.
ANSWER:
As Kukeltje pointed out in the comments I've made a complete nonsense in my main container and added an autoupdate component to it. That component messed up my data table events, loading data without a table to hold it. Once I've removed the component from my main container, everything worked out. Here is the code for my main container (commented out the troublemaker).
<div id="content-window">
<p:outputPanel id="content-panel">
<ui:include src="#{contentLoaderBean.mainContent}" />
<!-- <p:autoUpdate /> -->
</p:outputPanel>
</div>
The only situations I've seen this happening is when the datatable is fully updated in addtion to a partial update of the datatable via an event (page or filter or sort or...) In the rowSelect you do seem to update the full form which contains the datatable as well. That is bad practice and can as mentioned result in what you seem so remove that.
But...in your question there are no filter ajax events that explicitly update the full datatable so that cannot cause it. Yet with 99% vertainty there is something fully updates the datatable. Three options
there is something in your live code you left out of the datatable in your question
There is something else outside the code you posted that plays havoc (an autoupdate e.g),
an update from the server side is being done in a method
I am trying to make primefaces data table from this link (second table with just one selection).
This is my code for xhtml:
<h:form>
<p:dataTable value="#{security.data}" var="dat" selectionMode="single" selection="#{security.selected}" rowKey="#{dat.name}">
<f:facet name="header">
List of Customers
</f:facet>
<p:column headerText="Customer">
<h:outputText value="#{dat.name}" />
</p:column>
<p:column headerText="Contact">
<h:outputText value="#{dat.contact}" />
</p:column>
<f:facet name="footer">
<p:commandButton icon="ui-icon-search" value="View" action="#{security.editCustomer()}" />
</f:facet>
</p:dataTable>
</h:form>
Security is my managedbean with a list "data" and string variable "selected".
For some reason I am getting error that 'dat' is a string and 'name' is not its member. I am properly defining getter in the bean:-
private List<customerData> data;
public security()
{
data= new ArrayList<>();
data.add(new customerData("Name1","264"));
}
public List<customerData> getData()
{
return data;
}
"CustomerData" contains "name" attribute.
I have two primefaces dataTables. The first show the instances of a MainEntity.
The second one show a list of Words (just strings) associated with the selected MainEntity on the first datatable.
The printscreen illustrates what I mean.
My problem is, when I edit a string on the Word List, my listener method won't receive the new value. In fact, when I call the event.getNewValue() method, I get old value.
What am I missing?
I'm using JavaServer Faces 2.2, Primefaces 5.0 and Spring Framework 4.0.3.
Thanks in advance for any help.
The code for the xhtml, the managed bean and the MainEntity are as follows:
mainEntity.xhtml:
<h:body>
<h:form id="mainEntityForm">
<p:panelGrid columns="1" fullPage="true" id="dashboard">
<f:facet name="header">
<h2>MainEntity Table</h2>
</f:facet>
<p:panel>
<f:facet name="header">
MainEntity List
<p:commandButton value="New MainEntity"
actionListener="#{mainEntityController.createMainEntityDialog}"
styleClass="header-button">
<p:ajax event="dialogReturn" update=":mainEntityForm:mainEntityTable" />
</p:commandButton>
</f:facet>
<p:dataTable id="mainEntityTable" var="mainEntity" value="#{mainEntityController.mainEntities}"
editable="true" editMode="cell" widgetVar="cellMainEntity"
selectionMode="single" selection="#{mainEntityController.selectedMainEntity}"
rowKey="#{mainEntity.id}" tableStyle="width:auto">
<p:ajax event="rowSelect" update=":mainEntityForm:stringGrid :mainEntityForm:entityAGrid :mainEntityForm:entityBGrid" />
<p:ajax event="cellEdit" listener="#{mainEntityController.onEditMainEntity}" update=":mainEntityForm:mainEntityTable" />
<p:column headerText="ID">
<h:outputText value="#{mainEntity.id}" />
</p:column>
<p:column headerText="Name">
<p:cellEditor>
<f:facet name="output">
<h:outputText id="nameOutput" value="#{mainEntity.name}" />
</f:facet>
<f:facet name="input">
<h:inputText id="atyInput" value="#{mainEntity.qty}" />
</f:facet>
</p:cellEditor>
</p:column>
<p:column headerText="Commands">
<p:commandButton title="Remove MainEntity" icon="ui-icon-trash"
actionListener="#{mainEntityController.deleteMainEntity(mainEntity)}"
update=":mainEntityForm:mainEntityTable" />
</p:column>
</p:dataTable>
</p:panel>
<p:panelGrid fullPage="true" id="mainEntityDetail">
<f:facet name="header">
<p:row>
<p:column colspan="4">
<h2>MainEntity Detail</h2>
</p:column>
</p:row>
</f:facet>
<p:row>
<p:column>
<p:panel>
<f:facet name="header">
Word List
<p:commandButton value="New Word"
actionListener="#{mainEntityController.addNewWord()}"
styleClass="header-button"
update=":mainEntityForm:wordGrid">
</p:commandButton>
</f:facet>
<p:dataTable id="wordGrid" var="word"
value="#{mainEntityController.selectedMainEntity.wordList}"
tableStyle="width:auto" editable="true" editMode="cell" widgetVar="cellWord">
<p:ajax event="cellEdit" listener="#{mainEntityController.onEditWord}" />
<p:column headerText="Word">
<p:cellEditor>
<f:facet name="output">
<h:outputText id="wordOutput" value="#{word}" />
</f:facet>
<f:facet name="input">
<h:inputText id="wordInput" value="#{word}" />
</f:facet>
</p:cellEditor>
</p:column>
<p:column headerText="Commands">
<p:commandButton title="Remove Word"
icon="ui-icon-trash"
actionListener="#{mainEntityController.removeWord(word)}"
update=":mainEntityForm:wordGrid" />
</p:column>
</p:dataTable>
</p:panel>
</p:column>
</p:row>
</p:panelGrid>
</p:panelGrid>
</h:form>
</h:body>
MainEntityController.java (managed bean):
#ManagedBean
#SessionScoped
public class MainEntityController {
//...
private MainEntity selectedMainEntity;
public List<MainEntity> mainEntities;
//...
public MainEntity getSelectedMainEntity(){
return selectedMainEntity;
}
public void setSelectedMainEntity(MainEntity mainEntity){
this.selectedMainEntity = mainEntity;
}
//...
public void onEditWord(CellEditEvent event) {
Object oldValue = event.getOldValue();
Object newValue = event.getNewValue();
if(newValue != null && !newValue.equals(oldValue)) {
// THE PROBLEM IS HERE
// I NEVER ACTUALLY REACH THIS CODE
// newValue is always equal to oldValue!
FacesContext context = FacesContext.getCurrentInstance();
String word = context.getApplication().evaluateExpressionGet(context, "#{word}", String.class);
System.out.println(newValue);
System.out.println(word);
FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Word Changed", "Old: " + oldValue + ", New:" + newValue);
FacesContext.getCurrentInstance().addMessage(null, msg);
}
}
}
MainEntity.java:
#Entity
public class MainEntity {
#Id
private String id;
#ElementCollection(fetch=FetchType.EAGER)
#CollectionTable(
name="MainEntity_Word",
joinColumns = #JoinColumn(name = "id", referencedColumnName="id")
)
#Column(name = "word")
private Set<String> wordList;
//...
public void addWord(String word) {
this.wordList.add(word);
}
public void removeWord(String word) {
this.wordList.remove(word);
}
public Set<String> getWordList() {
return wordList;
}
public void setWordList(Set<String> wordList) {
this.wordList = wordList;
}
}
Change
<h:inputText id="wordInput" value="#{word}" />
to
<p:inputText id="wordInput" value="#{word}" />
or
<h:inputText id="wordInput" value="#{word}" >
<p:ajax event="change" process="#this"/>
</h:inputText>
You need to ajaxify the input component, and at last update the outputText component.
Edited:
change this column :
<p:column headerText="Word" >
<p:cellEditor>
<f:facet name="output">
<h:outputText id="wordOutput" value="#{word}" />
</f:facet>
<f:facet name="input">
<h:inputText id="wordInput" value="#{word}" />
</f:facet>
</p:cellEditor>
</p:column>
to
<p:column headerText="Word" >
<p:cellEditor>
<f:facet name="output">
<h:outputText id="wordOutput" value="#{word}" />
</f:facet>
<f:facet name="input">
<p:inputText id="wordInput" value="#{word}" >
<p:ajax event="change" update="wordOutput" />
</p:inputText>
</f:facet>
</p:cellEditor>
</p:column>
Edit 2 :
From Chapter 24 Introduction to the Java Persistence API
If an entity instance be passed by value as a detached object, such as
through a session bean’s remote business interface, the class must
implement the Serializable interface.
I finally got it working.
I suspected of the String immutable nature and what I did was to encapsulate the 'word' in a class like the following:
public class Word {
private String word;
Word (String word) {
this.setWord(word);
}
public String getWord() {
return this.word;
}
public void setWord(String word) {
this.word = word;
}
}
After that, I also had to create a set inside the managed bean (controller) to hold the set of words, like 'private Set managedWordList;'.
I also changed the getter for the list, inside the managed bean, to convert from the String set to a Word set like:
public Set<Word> getWordList() {
if (this.selectedMainEntity != null){
this.wordList = new HashSet<Word>();
for (String word : this.selectedMainEntity.getWordList()) {
this.wordList.add(new Word(word));
}
}
return this.wordList;
}
Finally, I changed the dataTable to reference the new set. From:
<p:dataTable ... value="#{mainEntityController.selectedMainEntity.wordList}"... />
To:
<p:dataTable ... value="#{mainEntityController.wordList}"... />
And only then I got it to work as I expected.
I am trying to use the feature of prime faces which allows user to edit the row data in the table itself.I have followed this link to achieve it:
http://www.primefaces.org/showcase/ui/datatableRowEditing.jsf
When I say edit user the new values which are entered are not sent to the bean.It still shows the old values only.
My code in JSF:
<p:dataTable value="#{mybean.userList}"
var="item"
id="dataTab"
widgetVar="usersTable"
tableStyleClass="data" paginator="true" rows="5"
filteredValue="#{userController.filteredUsers}"
editable="true"
rowKey="#{item}">
<p:ajax event="rowEdit" listener="#{mybean.onEdit}" update=":userForm:growl" />
<p:ajax event="rowEditCancel" listener="#{mybean.onCancel}" update=":userForm:growl" />
<f:facet name="header">
<p:outputPanel>
<h:outputText value="Search all fields:" />
<p:inputText id="globalFilter" onkeyup="('usersTable').filter()" style="width:150px" />
</p:outputPanel>
</f:facet>
<p:column sortBy="#{item.firstName}" filterBy="#{item.firstName}"
filterMatchMode="startsWith">
<p:cellEditor>
<f:facet name="header">
<h:outputText value="First Name" />
</f:facet>
<f:facet name="output">
<h:outputText value="#{item.firstName}" />
</f:facet>
<f:facet name="input">
<p:inputText value="#{item.firstName}" style="width:100%"/>
</f:facet>
</p:cellEditor>
</p:column>
<p:column sortBy="#{item.lastName}" filterBy="#{item.lastName}" filterMatchMode="startsWith">
<p:cellEditor>
<f:facet name="header">
<h:outputText value="Last Name" />
</f:facet>
<p:column headerText="Update" style="width:6%">
<p:rowEditor />
</p:column>
</p:dataTable>
My code in bean:
public String onEdit(RowEditEvent event) {
User user=(User)event.getObject());
user.getFirstName();
}
code in the bean for getting the list in ui:
public List<UserBean> getUsersList(){
List<UserBean> retval = new ArrayList<>();
for (Object[] tuple : myFacade.getUserList()) {
UserBean ub = new UserBean();
ub.setFirstName((String) tuple[0]);
ub.setLastName((String)tuple[1]);
ub.setEmailAddress((String)tuple[2]);
ub.setOfficeNumber((String)tuple[3]);
ub.setRole((String)tuple[4]);
retval.add(ub);
}
return retval;
}
I have tried the suggestions that were given in some of the posts but they did not work.Could anyone let me know how can I get the new values.I am using glassfish 4.0,primefaces 3.5.
I was able to figure out the problem..Every time when I am getting the list in the getter method I am calling the database to load the data..But this should be done only if the list is null.The following code gives you a clear picture:
public List<UserBean> getUsersList(){
if(retval == null)
{
retval = new ArrayList<>();
for (Object[] tuple : myFacade.getUserList()) {
UserBean ub = new UserBean();
ub.setFirstName((String) tuple[0]);
ub.setLastName((String)tuple[1]);
ub.setEmailAddress((String)tuple[2]);
ub.setOfficeNumber((String)tuple[3]);
ub.setRole((String)tuple[4]);
retval.add(ub);
}
}
return retval;
}
So getUsersList is my value of var in the p:datatable and now when I call the onEdit it will check for the list if it is null it does a database call or does not call the database.