0

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:

  1. Select the drop down value

enter image description here

  1. input the filter so that all the rows are filter out

enter image description here

  1. clear the filter, the selected value disappear

enter image description here

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

saycchai
  • 57
  • 8
  • 1
  • and `update="@this"` in the sort/page/filter ajax events does not do a lot if you expected that. They do not limit the update to the ajax component or the datatable (and not its content). They update the whole table by default (in addition to things outside the datatable if addressed via id or other selectors) and I've seen cases where explicitly updating a datatable (or other component) from withing an ajax inside it, messes things up. Effectively you do not save the data before filtering/paging/sorting and the solution by @JasperDeVries is the way to go. – Kukeltje Aug 04 '20 at 11:42
  • @JasperdeVries I tried `` and also tried ``, same result, the selected value disappear after clear the filter – saycchai Aug 05 '20 at 01:17
  • @Kukeltje I use `skipChildren="true"` in the sort/page/filter to save my value to backing bean on ajax, I tried the input text and text area can keep the value, only selectOneMenu lost the value for filtering (if there is at least one row remain in the filtering and clear the filter, then all the selected value of the drop down can be kept, only when no row remain in the filter and clear the filter will lost all the selected value in the drop down). Please also see my trial for @JasperdeVries suggestion in the previous comment – saycchai Aug 05 '20 at 01:22
  • @JasperdeVries I also tried `` still same result (lost the selected value on filtering and then clear the filter) – saycchai Aug 05 '20 at 01:28
  • @Kukeltje sorry should be I use `skipChildren="false"` – saycchai Aug 05 '20 at 01:47
  • Summary: 1: you use `skipChildren="false"` (it is the default btw) like in the question, 2: it is really**_only_** a problem with `p:selectOneMenu` and not with a `p:inputText` (can you add that in the code in the question (same code) New 'questions': 1: Does it only happen when filtering and not when sorting? 2: Tried removing the `global="false" on the ajax filter 3:Tried removing the `update="@this"`? 4: What is your PF version? – Kukeltje Aug 05 '20 at 06:59
  • Sorry, PF 8, I sort of knew you already wrote it but remarkably I could not find it... sorry – Kukeltje Aug 05 '20 at 07:19
  • @Kukeltje please see my updated in the question for the input text code and screen cap (input either filter get the same result - the selected value of the drop down is lost, input text can retain the value). For 1, sorting no problem, only filtering. Both 2 and 3 are tried, same result (the selected value is lost). For 4, Primefaces 8.0 – saycchai Aug 05 '20 at 12:20
  • Thanks! And if you replace the `p:selectOneMenu` with an `h:selectOneMenu`? Or try without converting to an `entrySet()` in EL but do that in the bean once on constructing it. And if 2 and 3 do not make a difference, remove them from the code. So it becomes more minimal but still reproducible! (thanks for working with us and the quick response. Feels good for a difference, not all do this (not by a long shot)) – Kukeltje Aug 05 '20 at 12:49
  • And if you set a breakpoint in the setDd and getDd, are they called ? – Kukeltje Aug 05 '20 at 13:14
  • @Kukeltje thx for your help! Please see my work around in the answer. – saycchai Aug 06 '20 at 07:43
  • Hi, I'm not interested in a workaround (not meant in a negative way, and it is a really creative solution that shows knowledge ;-) ) I want to get to the details of the real issue so it can just be fixed. Therefore please answer the last two comments... – Kukeltje Aug 06 '20 at 07:46
  • @Kukeltje I reported this issue to PrimeFaces: https://github.com/primefaces/primefaces/issues/6196, so may be let them to check out what the actual problem is. – saycchai Aug 06 '20 at 07:59
  • ok, great! but see first thing they tried is with a plain `h:selectOneMenu`? Moving the entrySet() from the EL/View to the bean and doing it once would be the next thing to try. – Kukeltje Aug 06 '20 at 08:36

1 Answers1

0

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>

saycchai
  • 57
  • 8