I have the datatable inside the rowExpansion and I can select rows of that table by means of checkboxes. I select some of them. But after I click on the row-toggler icon to untoggle inner table the event resets checkboxes in "false".
Can I avoid this behavior ?
Car Brand:
import java.util.List;
public class CarBrand {
private final String brandName;
private final List<CarModel> models;
private boolean selected;
public CarBrand(String brandName, List<CarModel> models) {
this.brandName = brandName;
this.models = models;
}
public String getBrandName() {
return brandName;
}
public List<CarModel> getModels() {
return models;
}
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
}
Car Model:
public class CarModel {
private final String modelName;
private final int year;
private boolean selected;
public CarModel(String modelName, int year) {
this.modelName = modelName;
this.year = year;
}
public String getModelName() {
return modelName;
}
public int getYear() {
return year;
}
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
}
CDI Managed Bean:
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.enterprise.context.SessionScoped;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.event.AjaxBehaviorEvent;
import javax.inject.Named;
import org.primefaces.event.ToggleEvent;
import org.primefaces.model.Visibility;
#Named
#SessionScoped
public class Bean implements Serializable {
List<CarBrand> brands;
#PostConstruct
private void init() {
brands = new ArrayList<>();
List<CarModel> carModels;
carModels = new ArrayList<>();
carModels.add(new CarModel("CL 63 AMG", 2011, 1));
carModels.add(new CarModel("S-Class Coupe", 2014, 2));
carModels.add(new CarModel("AMG GTS", 2015, 3));
brands.add(new CarBrand("Mercedes", carModels));
carModels = new ArrayList<>();
carModels.add(new CarModel("M5 F10", 2012, 4));
carModels.add(new CarModel("M6 F13", 2012, 5));
carModels.add(new CarModel("X5 M", 2015, 6));
brands.add(new CarBrand("BMW", carModels));
carModels = new ArrayList<>();
carModels.add(new CarModel("Polo", 2012, 7));
carModels.add(new CarModel("Golf GTI", 2013, 8));
carModels.add(new CarModel("Golf R", 2015, 9));
brands.add(new CarBrand("Volkswagen", carModels));
}
public void onRowToggle(AjaxBehaviorEvent event) {
ToggleEvent toggleEvent = (ToggleEvent) event;
FacesMessage msg;
if (toggleEvent.getVisibility() == Visibility.VISIBLE) {
msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Toggled", "Visibility:" + toggleEvent.getVisibility());
} else {
msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Untoggled", "Visibility:" + toggleEvent.getVisibility());
}
FacesContext.getCurrentInstance().addMessage(null, msg);
}
public List<CarBrand> getBrands() {
return brands;
}
}
and .xhtml
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:p="http://primefaces.org/ui">
<f:view>
<h:head>
<title>TEST</title>
</h:head>
<h:body>
<h:form id="testForm" prependId="true">
<p:growl id="growl"/>
<p:dataTable id="carsTable" value="#{bean.brands}" var="brand" style="width: 400px">
<p:ajax event="rowToggle" listener="#{bean.onRowToggle}" update="testForm:growl"/>
<p:column style="width: 20px">
<p:rowToggler/>
</p:column>
<p:column headerText="Brand">
<b><h:outputText value="#{brand.brandName}"/></b>
</p:column>
<p:rowExpansion>
<p:dataTable id="modelsTable" value="#{brand.models}" var="model">
<p:column headerText="Model">
<h:outputText value="#{model.modelName}"/>
</p:column>
<p:column headerText="Year">
<h:outputText value="#{model.year}"/>
</p:column>
<p:column style="width: 20px">
<f:facet name="header">
<p:selectBooleanCheckbox id="selectAll" value="#{brand.selected}" rendered="#{not empty brand.models}">
<p:ajax/>
</p:selectBooleanCheckbox>
</f:facet>
<p:selectBooleanCheckbox id="modelSelect" value="#{model.selected}">
<p:ajax/>
</p:selectBooleanCheckbox>
</p:column>
</p:dataTable>
</p:rowExpansion>
</p:dataTable>
</h:form>
</h:body>
</f:view>
The problem is even worse than you mention. It is caused by the implementation of the toggle. If you see what is going on in the DOM, the expanded items get removed completely. When you toggle it back, the object are re-rendered.
For some reason text values are preserved, but the checkboxes get reverted to their original value.
The situation is worth though. If you do not expand the expansion, then the not rendered elements are not submitted, which for checkboxes means their values are changed to "false" no matter what was their original value.
You have basically two options:
move checkboxes outside the expansion
store their value using Javascript outside their expanded area
Related
I have a drop down (p:selectOneMenu) as the input field in the Primefaces 8.0 datatable row, after I select the value in the drop down, if I sort it the selected value can be kept after ajax submit. However if I input a filter that filter 0 rows, and then I clear the filter, the selected value in the drop down disappear:
updated base on Kukeltje's request for adding input text:
Select the drop down value
input the filter so that all the rows are filter out
clear the filter, the selected value disappear
My backing bean:
package sample;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
#Named
#SessionScoped
public class SampleBean implements java.io.Serializable {
/**
*
*/
private static final long serialVersionUID = 5307652891294044974L;
private static final Map<String, String> dropDown = new HashMap<>();
static {
dropDown.put("K1", "K1");
dropDown.put("K2", "K2");
dropDown.put("K3", "K3");
}
public Map<String, String> getDropDown() {
return dropDown;
}
private List<TableObject> tableObjects = Arrays.asList(new TableObject[] {new TableObject(), new TableObject()});
public List<TableObject> getTableObjects() {
return tableObjects;
}
public void setTableObjects(List<TableObject> tableObjects) {
this.tableObjects = tableObjects;
}
public static class TableObject
{
private String dd;
private String inputText;
public String getDd() {
return dd;
}
public void setDd(String dd) {
this.dd = dd;
}
public String getInputText() {
return inputText;
}
public void setInputText(String inputText) {
this.inputText = inputText;
}
}
}
My facelet:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui" xmlns:o="http://omnifaces.org/ui"
xmlns:of="http://omnifaces.org/functions">
<h:head>
<title>Hello World JSF 2.3</title>
</h:head>
<h:body>
<h:form>
<p:dataTable var="item" value="#{sampleBean.tableObjects}"
widgetVar="itemTable">
<p:ajax event="sort" process="#this" update="#this"
skipChildren="false" />
<p:ajax event="page" process="#this" update="#this"
skipChildren="false" />
<p:ajax event="filter" process="#this" update="#this" global="false"
skipChildren="false" />
<p:column headerText="Dropdown" sortBy="#{item.dd}"
filterBy="#{item.dd}" filterMatchMode="contains">
<p:selectOneMenu id="Dropdown" value="#{item.dd}" required="false"
label="Dropdown" style="width: 90%">
<f:selectItem itemValue="#{null}" itemLabel="" />
<f:selectItems value="#{sampleBean.dropDown.entrySet()}"
var="entry" itemValue="#{entry.value}" itemLabel="#{entry.key}" />
</p:selectOneMenu>
</p:column>
<p:column id="InputTextHeader" headerText="Input Text"
sortBy="#{item.inputText}" filterBy="#{item.inputText}"
filterMatchMode="contains">
<p:inputText id="InputText" value="#{item.inputText}" />
</p:column>
</p:dataTable>
</h:form>
</h:body>
</html>
I push my testing project to github, in case you want to test it
git clone https://github.com/saycchai/jsf-test.git
cd jsf-test
chmod +x *.sh
./buildAndRun.sh
for payara server:
browse: http://localhost:8080/index.xhtml
for other server:
http://localhost:8080/jsf-test/index.xhtml
Finally I found a work around solution as follows:
I added a phase listener, if the filter submit 0 row (pass as a request parameter, please see the onstart part in the facelet), then backup the Dd value to previousDd before the APPLY_REQUEST_VALUES phase and set the backup value previousDd back to Dd before the RENDER_RESPONSE phase, code as follows:
Backing Bean
package sample;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
import javax.persistence.Transient;
#Named
#SessionScoped
public class SampleBean implements java.io.Serializable {
/**
*
*/
private static final long serialVersionUID = 5307652891294044974L;
private static final Map<String, String> dropDown = new HashMap<>();
static {
for(int i=1; i<10; i++) {
dropDown.put("K"+i, "K"+i);
}
}
public Map<String, String> getDropDown() {
return dropDown;
}
private List<TableObject> tableObjects = Arrays.asList(new TableObject[] {new TableObject(), new TableObject(), new TableObject(), new TableObject()});
public List<TableObject> getTableObjects() {
return tableObjects;
}
public void setTableObjects(List<TableObject> tableObjects) {
this.tableObjects = tableObjects;
}
public static class TableObject
{
private String dd;
private String inputText;
public String getDd() {
return dd;
}
public void setDd(String dd) {
this.dd = dd;
}
public String getInputText() {
return inputText;
}
public void setInputText(String inputText) {
this.inputText = inputText;
}
#Transient
private String previousDd;
public String getPreviousDd() {
return previousDd;
}
public void setPreviousDd(String previousDd) {
this.previousDd = previousDd;
}
}
}
Facelet
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui"
>
<h:head>
<title>Hello World JSF 2.3</title>
</h:head>
<h:body>
<h:form id="form">
<p:dataTable id="itemTable" var="item" value="#{sampleBean.tableObjects}"
widgetVar="itemTable"
paginator="true"
>
<p:ajax event="sort" process="#this" update="#this"
skipChildren="false" />
<p:ajax event="page" process="#this" update="#this"
skipChildren="false"
/>
<p:ajax event="filter" process="#this" update="#this" global="false"
skipChildren="false"
onstart="cfg.ext.params.push({name: 'tableFilterCount', value: PF('itemTable').paginator.cfg.rowCount});"
/>
<p:column id="DropdownHeader" headerText="Dropdown" sortBy="#{item.dd}"
filterBy="#{item.dd}" filterMatchMode="contains">
<p:selectOneMenu id="Dropdown" value="#{item.dd}" required="false"
label="Dropdown" style="width: 90%">
<f:selectItem itemValue="#{null}" itemLabel="" />
<f:selectItems value="#{sampleBean.dropDown.entrySet()}"
var="entry" itemValue="#{entry.value}" itemLabel="#{entry.key}" />
</p:selectOneMenu>
</p:column>
<p:column id="InputTextHeader" headerText="Input Text"
sortBy="#{item.inputText}" filterBy="#{item.inputText}"
filterMatchMode="contains">
<p:inputText id="InputText" value="#{item.inputText}" >
<p:ajax />
</p:inputText>
</p:column>
</p:dataTable>
</h:form>
</h:body>
</html>
Phase Listener
package sample;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import org.primefaces.component.datatable.DataTable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.SampleBean.TableObject;
public class SamplePhaseListener implements PhaseListener {
/**
*
*/
private static final long serialVersionUID = 5273254619684337785L;
private Logger logger = LoggerFactory.getLogger(this.getClass());
public void afterPhase(PhaseEvent event) {
}
public void beforePhase(PhaseEvent event) {
if(event.getPhaseId() == PhaseId.APPLY_REQUEST_VALUES) {
prettyPrint("----------------start of Before "+event.getPhaseId().getName()+"------------------------------", 0);
String tableFilterCount = event.getFacesContext().getExternalContext().getRequestParameterMap().get("tableFilterCount");
if(tableFilterCount != null && "0".equals(tableFilterCount)) {
//backup the previous value first
DataTable table = (DataTable) event.getFacesContext().getViewRoot().findComponent("form:itemTable");
for (int index = 0; index < table.getRowCount(); index++) {
table.setRowIndex(index);
Object rowData = table.getRowData();
if(rowData != null) {
TableObject tableObject = (TableObject) rowData;
logger.info(" before backup to previousDd, dd: {}, previousDd: {}", tableObject.getDd(), tableObject.getPreviousDd());
tableObject.setPreviousDd(tableObject.getDd());
logger.info(" after backup to previousDd, dd: {}, previousDd: {}", tableObject.getDd(), tableObject.getPreviousDd());
}
}
}
prettyPrint("----------------end of Before "+event.getPhaseId().getName()+"------------------------------", 0);
}
if(event.getPhaseId() == PhaseId.RENDER_RESPONSE) {
prettyPrint("----------------start of Before " + event.getPhaseId().getName() + "------------------------------", 0);
String tableFilterCount = event.getFacesContext().getExternalContext().getRequestParameterMap().get("tableFilterCount");
if(tableFilterCount != null && "0".equals(tableFilterCount)) {
//restore the Dd from previous value
DataTable table = (DataTable) event.getFacesContext().getViewRoot().findComponent("form:itemTable");
for (int index = 0; index < table.getRowCount(); index++) {
table.setRowIndex(index);
Object rowData = table.getRowData();
if(rowData != null) {
TableObject tableObject = (TableObject) rowData;
logger.info(" before restore from previousDd, dd: {}, previousDd: {}", tableObject.getDd(), tableObject.getPreviousDd());
tableObject.setDd(tableObject.getPreviousDd());
logger.info(" after restore from previousDd, dd: {}, previousDd: {}", tableObject.getDd(), tableObject.getPreviousDd());
}
}
}
prettyPrint("----------------end of Before "+event.getPhaseId().getName()+"------------------------------", 0);
}
}
public PhaseId getPhaseId() {
return PhaseId.ANY_PHASE;
}
public static final String IDENT = " ";
private void prettyPrint(String str, int depth) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < depth; i++)
sb.append(IDENT);
sb.append(str + "\n");
logger.trace(sb.toString());
}
}
faces-config.xml
<?xml version='1.0' encoding='UTF-8'?>
<faces-config xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_3.xsd"
version="2.3">
<lifecycle>
<phase-listener>sample.SamplePhaseListener</phase-listener>
</lifecycle>
</faces-config>
What I doing is just an application where the selected option display in the textArea. But Ajax is not working after lending the page after navigation from the main menu on this page. That function only works after using the submit button.
Below is my JSF page code
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui"
template="/WEB-INF/template.xhtml">
<ui:define name="title">
Ajax Framework - <span class="subitem">Basic</span>
</ui:define>
<ui:define name="description">
This example demonstrates a simple but common usage of posting the form, updating the backend value and displaying the output with ajax.
</ui:define>
<ui:define name="implementation">
<h:form>
<p:panel id="panel" header="Form" style="margin-bottom:10px;">
<p:messages>
<p:autoUpdate />
</p:messages>
<h:panelGrid columns="2" style="margin-bottom:10px" cellpadding="5">
<p:outputLabel for="lazy" value="Lazy:" />
<p:selectOneMenu id="lazy" value="#{selectOneMenuView.option}" lazy="true" style="width:125px">
<f:selectItem itemLabel="Select One" itemValue="" />
<f:selectItems value="#{selectOneMenuView.options}" />
<f:ajax event="change" listener="#{selectOneMenuView.onChange}" execute="#this" render="textarea1"/>
</p:selectOneMenu>
</h:panelGrid>
<p:commandButton value="Submit" update="display" oncomplete="PF('dlg').show()" icon="ui-icon-check" />
<p:dialog header="Values" modal="true" showEffect="bounce" widgetVar="dlg" resizable="false">
<p:panelGrid columns="2" id="display" columnClasses="label,value">
<h:outputText value="Lazy:" />
<h:outputText value="#{selectOneMenuView.option}" />
</p:panelGrid>
</p:dialog>
<h3>AutoResize</h3>
<p:inputTextarea id="textarea1" value="#{selectOneMenuView.inputTextArea}" rows="6" cols="33" />
</p:panel>
</h:form>
</ui:define>
</ui:composition>
And my managed bean is as below
package org.primefaces.showcase.view.input;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.model.SelectItem;
import javax.faces.model.SelectItemGroup;
import org.primefaces.showcase.domain.Theme;
import org.primefaces.showcase.service.ThemeService;
#ManagedBean
public class SelectOneMenuView {
private String console;
private String car;
private List<SelectItem> cars;
private String city;
private Map<String,String> cities = new HashMap<String, String>();
private Theme theme;
private List<Theme> themes;
private String option;
private List<String> options;
private String inputTextArea;
public String getInputTextArea() {
return inputTextArea;
}
public void setInputTextArea(String inputTextArea) {
this.inputTextArea = inputTextArea;
}
#ManagedProperty("#{themeService}")
private ThemeService service;
#PostConstruct
public void init() {
//cars
SelectItemGroup g1 = new SelectItemGroup("German Cars");
g1.setSelectItems(new SelectItem[] {new SelectItem("BMW", "BMW"), new SelectItem("Mercedes", "Mercedes"), new SelectItem("Volkswagen", "Volkswagen")});
SelectItemGroup g2 = new SelectItemGroup("American Cars");
g2.setSelectItems(new SelectItem[] {new SelectItem("Chrysler", "Chrysler"), new SelectItem("GM", "GM"), new SelectItem("Ford", "Ford")});
cars = new ArrayList<SelectItem>();
cars.add(g1);
cars.add(g2);
//cities
cities = new HashMap<String, String>();
cities.put("New York", "New York");
cities.put("London","London");
cities.put("Paris","Paris");
cities.put("Barcelona","Barcelona");
cities.put("Istanbul","Istanbul");
cities.put("Berlin","Berlin");
//themes
themes = service.getThemes();
//options
options = new ArrayList<String>();
for(int i = 0; i < 20; i++) {
options.add("Option " + i);
}
}
public String getConsole() {
return console;
}
public void setConsole(String console) {
this.console = console;
}
public String getCar() {
return car;
}
public void setCar(String car) {
this.car = car;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public Theme getTheme() {
return theme;
}
public void setTheme(Theme theme) {
this.theme = theme;
}
public List<SelectItem> getCars() {
return cars;
}
public Map<String, String> getCities() {
return cities;
}
public List<Theme> getThemes() {
return themes;
}
public void setService(ThemeService service) {
this.service = service;
}
public String getOption() {
return option;
}
public void setOption(String option) {
this.option = option;
}
public List<String> getOptions() {
return options;
}
public void setOptions(List<String> options) {
this.options = options;
}
public void onChange(){
this.inputTextArea = this.option;
}
}
May I know why? Can anyone give me some guideline?
I'm trying to create a DataTables with Cell Editing with Click such as presented here : https://www.primefaces.org/showcase/ui/data/datatable/edit.xhtml
The problem I have is that the table won't take modifications into account.
I have no problem displaying the datas from the MySQL Database. The table is loaded correctly with no errors. When I try modifying a cell, it becomes an input as expected. But when I change the value and press enter, the cell goes back to it's original value. If I re-enter edit mode on that cell, my modification reappears. Not modifications are made in the Database.
I'm not very at ease with JSF and PrimeFaces and I'm surely missing something obvious.
Here is my code :
Managed Bean :
package Application;
import java.io.Serializable;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.inject.Named;
import javax.faces.view.ViewScoped;
import org.primefaces.event.CellEditEvent;
import org.primefaces.event.RowEditEvent;
#Named(value = "plantCtrl")
#ViewScoped
public class PlantCtrl implements Serializable {
#EJB
private PlantDAO plantDAO;
public PlantCtrl() {
}
#PostConstruct
public void init(){
}
public List<Plant> getPlants() {
return plantDAO.allPlants();
}
public PlantDAO getPlantDAO() {
return plantDAO;
}
public void setPlantDAO(PlantDAO plantDAO) {
this.plantDAO = plantDAO;
}
public void onRowEdit(RowEditEvent event) {
FacesMessage msg = new FacesMessage("Plant Edited");
FacesContext.getCurrentInstance().addMessage(null, msg);
}
public void onRowCancel(RowEditEvent event) {
FacesMessage msg = new FacesMessage("Edit Cancelled");
FacesContext.getCurrentInstance().addMessage(null, msg);
}
public void onCellEdit(CellEditEvent event) {
Object oldValue = event.getOldValue();
Object newValue = event.getNewValue();
if(newValue != null && !newValue.equals(oldValue)) {
FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Cell Changed", "Old: " + oldValue + ", New:" + newValue);
FacesContext.getCurrentInstance().addMessage(null, msg);
}
}
}
PlantDAO :
package Application;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
#Stateless
public class PlantDAO {
#PersistenceContext(unitName = "CarnivorousGardenPU")
private EntityManager em;
public List<Plant> allPlants() {
Query query = em.createNamedQuery("Plant.findAll");
return query.getResultList();
}
public void add(Plant p) {
em.persist(p);
em.flush();
}
public void edit(Plant p) {
em.merge(p);
em.flush();
}
public void remove(Plant p) {
em.remove(em.merge(p));
em.flush();
}
}
XHTML :
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<h:head>
<title>Dashboard</title>
</h:head>
<h:body>
<ui:include src="inc/header.xhtml" />
<h:form id="plantAdminList">
<p:growl id="msgs" showDetail="true"/>
<p:dataTable id="plantsTable" var="plant" value="#{plantCtrl.plants}" editable="true" editMode="cell" widgetVar="cellPlants">
<f:facet name="header">
Cell Editing with Click and RightClick
</f:facet>
<p:ajax event="cellEdit" listener="#{plantCtrl.onCellEdit}" update=":plantAdminList:msgs, :plantAdminList:plantsTable" />
<p:column headerText="Id">
<p:cellEditor>
<f:facet name="output"><h:outputText value="#{plant.genusPlant}" /></f:facet>
<f:facet name="input"><p:inputText id="modelInput" value="#{plant.genusPlant}" style="width:96%"/></f:facet>
</p:cellEditor>
</p:column>
<p:column headerText="Price">
<p:cellEditor>
<f:facet name="output"><h:outputText value="#{plant.pricePlant}" /></f:facet>
<f:facet name="input"><p:inputText value="#{plant.pricePlant}" style="width:96%" label="Price"/></f:facet>
</p:cellEditor>
</p:column>
</p:dataTable>
</h:form>
<ui:include src="/inc/footer.xhtml" />
</h:body>
</html>
Plant.java is generated automatically by Netbeans as an Entity Classes From Database. I'm not posting it for not overloading this post but if it is needed, don't hesitate to ask.
Modify few lines of your managed bean like this
//....
List<Plant> plants;
#PostConstruct
public void init(){
plants=plantDAO.allPlants();
}
public List<Plant> getPlants() {
return plants;
}
//....
and it will work as you want: when you change the value and press enter, the cell will reflect your changes.
Notice that:
if you want to persist changes to database you will need to modify your p:dataTable a bit and create additional logic in managed bean to detect modifications and to pass them to already existing plantDao.edit method. Minimum of coding would be:
Modify data table like this
p:dataTable id="plantsTable" var="plant" value="#{plantCtrl.plants}" editable="true" widgetVar="cellPlants">
<p:ajax event="rowEdit" listener="#{plantCtrl.onRowEdit}"
update=":plantAdminList:msgs, :plantAdminList:plantsTable" />
....
<p:column style="width:32px">
<p:rowEditor />
</p:column>
</p:dataTable>
Modify onEditMethod inside managed bean
public void onRowEdit(RowEditEvent event) {
Plant editedPlant = (Plant)event.getObject();
plantDAO.edit(plant);
FacesMessage msg = new FacesMessage("Plant Edited");
FacesContext.getCurrentInstance().addMessage(null, msg);
}
I am basing my example on the PrimeFaces showcase example for the p:dataTableContextMenu example
The difference being I am trying to delete via a p:confirmDialog but the selected item is always null.
Here's a cut down example
The XHTML
<!DOCTYPE html>
<html xmlns="http://www.w3c.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
<title>Example</title>s
</h:head>
<h:body>
<h:form>
<p:confirmDialog widgetVar="cd" severity="alert" header="Confirmation"
message="Are you sure you wish to delete this car?">
<p:commandButton value="Yes" action="#{carTableView.deleteCar}"
update=":dataForm" oncomplete="PF('cd').hide();" />
<p:commandButton value="No" onclick="PF('cd').hide();" type="button" />
</p:confirmDialog>
</h:form>
<h:form id="dataForm">
<p:contextMenu for="cars">
<p:menuitem value="Delete" icon="ui-icon-close"
onclick="PF('cd').show(); return false;" />
<!-- action="#{formsView.deleteForm}" update=":dataForm" /> -->
</p:contextMenu>
<p:dataTable id="cars" var="car" value="#{carTableView.cars}"
rowKey="#{car.id}" selection="#{carTableView.selectedCar}"
selectionMode="single">
<f:facet name="header">
RightClick to View Options
</f:facet>
<p:column headerText="Id">
<h:outputText value="#{car.id}" />
</p:column>
<p:column headerText="Name">
<h:outputText value="#{car.name}" />
</p:column>
</p:dataTable>
</h:form>
</h:body>
</html>
The Model
public class Car
{
private String id;
private String name;
public Car(String id, String name)
{
this.id = id;
this.name = name;
}
public String getId()
{
return id;
}
public void setId(String id)
{
this.id = id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
}
And the bean
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#SuppressWarnings("serial")
#ManagedBean
#ViewScoped
public class CarTableView implements Serializable
{
private List<Car> cars;
private Car selectedCar;
#PostConstruct
public void init()
{
cars = createCars();
}
private List<Car> createCars()
{
List<Car> list = new ArrayList<Car>();
for (int i = 0; i < 10; i++)
{
list.add(new Car(UUID.randomUUID().toString().substring(0, 8), "Car " + String.valueOf(i + 1)));
}
return list;
}
public void deleteCar()
{
cars.remove(selectedCar);
selectedCar = null;
}
public List<Car> getCars()
{
return cars;
}
public void setCars(List<Car> cars)
{
this.cars = cars;
}
public Car getSelectedCar()
{
return selectedCar;
}
public void setSelectedCar(Car selectedCar)
{
this.selectedCar = selectedCar;
}
}
Now it seems to me that it's the involvement of running the deleteCar action from the p:confirmDialog that is the issue.
I say this as if I change
<p:menuitem value="Delete" icon="ui-icon-close"
onclick="PF('cd').show(); return false;" />
To
<p:menuitem value="Delete" icon="ui-icon-close"
action="#{formsView.deleteForm}" update=":dataForm" />
Then it works. In the p:confirmDialog example the selectedCar in the deleteCar method is always null. Despite specifying a rowKey attribute in p:dataTable
Since you have two forms, enclose <h:setPropertyActionListener > to your <p:menuitem>
Answer: The p:confirmDialog needs to be part of the same h:form as the p:dataTable
I would like to limit the number of rows a user can select to 4 and require a minimum of 1 one to be selected. Can this be done with a Primefaces DataTable?
This can easily be done using Primefaces Datatable. I've done an example for you below
XHTML Code:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui">
<h:head>
<title>Test</title>
</h:head>
<h:body>
<h:form id="form">
<p:dataTable value="#{tBean.availablePersonList}" var="person" id="table"
selection="#{tBean.selectedPersonList}" selectionMode="multi"
rowKey="#{person.name}">
<p:ajax event="rowSelect" listener="#{tBean.rowSelect}" update=":form:table"/>
<p:column headerText="Name">
#{person.name}
</p:column>
<p:column headerText="Address">
#{person.address}
</p:column>
</p:dataTable>
<p:commandButton value="Submit" actionListener="#{tBean.submit}"></p:commandButton>
</h:form>
</h:body>
</html>
Person Class
public class Person {
private String name;
private String address;
public Person(String name, String address) {
super();
this.name = name;
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
ManagedBean Code:
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.event.ActionEvent;
import org.primefaces.context.RequestContext;
import org.primefaces.event.SelectEvent;
#ManagedBean(name = "tBean")
#ViewScoped
public class TestBean implements Serializable {
private List<Person> availablePersonList;
private List<Person> selectedPersonList;
public TestBean() {
availablePersonList = new ArrayList<Person>();
availablePersonList.add(new Person("John", "London"));
availablePersonList.add(new Person("Pat", "London"));
availablePersonList.add(new Person("Meerkut", "Houston"));
availablePersonList.add(new Person("Ali", "London"));
availablePersonList.add(new Person("Parker", "Edinburgh"));
availablePersonList.add(new Person("Laurent", "Paris"));
}
public void submit(ActionEvent e) {
if (selectedPersonList.size() < 1) {
RequestContext.getCurrentInstance().showMessageInDialog(new FacesMessage("Select at least one item"));
return;
}
}
public void rowSelect(SelectEvent event) {
System.out.println(selectedPersonList.size());
if (selectedPersonList.size() > 3) {
selectedPersonList.remove(event.getObject());
RequestContext.getCurrentInstance().showMessageInDialog(new FacesMessage("You cannot selected more than 3"));
return;
}
}
public List<Person> getAvailablePersonList() {
return availablePersonList;
}
public void setAvailablePersonList(List<Person> availablePersonList) {
this.availablePersonList = availablePersonList;
}
public List<Person> getSelectedPersonList() {
return selectedPersonList;
}
public void setSelectedPersonList(List<Person> selectedPersonList) {
this.selectedPersonList = selectedPersonList;
}
}
Outcome:
in this example, we iterate all checkboxes and add a click event listener to each one. if checkbox is clicked then we check selected row count.
<h:form id="form">
<p:dataTable var="car" value="#{listBean.cars}"
selection="#{listBean.selectedCars}"
rowKey="#{car.id}"
paginator="true" rows="10"
widgetVar="myDataTable">
<p:column selectionMode="multiple" style="width:2%;text-align:center"/>
<p:column headerText="Id">
<h:outputText value="#{car.id}"/>
</p:column>
<p:column headerText="Year">
<h:outputText value="#{car.year}"/>
</p:column>
<p:column headerText="Manufacturer">
<h:outputText value="#{car.manufacturer}"/>
</p:column>
<p:column headerText="Model">
<h:outputText value="#{car.model}"/>
</p:column>
<p:column headerText="Color">
<h:outputText value="#{car.color}"/>
</p:column>
</p:dataTable>
<script type="text/javascript">
var MAX_ROW_SELECTION_COUNT = 4;
$(function () {
if (myDataTable.isSelectionEnabled()) {
var dataTableId = myDataTable.jqId;
// hide check all button
$(dataTableId + ' thead:first > tr > th.ui-selection-column .ui-chkbox-all').hide();
var checkboxes = $(dataTableId + ' tbody.ui-datatable-data:first > tr > td.ui-selection-column .ui-chkbox-box');
checkboxes.each(function (index, element) {
var chckbx = $(element);
console.log(chckbx);
chckbx.on("click", function (e) {
var disabled = chckbx.hasClass('ui-state-disabled'),
checked = chckbx.hasClass('ui-state-active');
if (!(checked || disabled)) {
if (myDataTable.getSelectedRowsCount() >= MAX_ROW_SELECTION_COUNT) {
alert('You cannot select more than ' + MAX_ROW_SELECTION_COUNT +' rows.');
return false;
}
}
});
});
}
});
</script>
</h:form>