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:
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>