下拉作为数据输入的 Primefaces 数据表过滤问题

Primefaces Datatable filtering issue for drop down as data input

我有一个下拉列表 (p:selectOneMenu) 作为 Primefaces 8.0 数据表行中的输入字段,在我 select 下拉列表中的值之后,如果我对它进行排序selected 值可以在 ajax 提交后保留。但是,如果我输入一个过滤 0 行的过滤器,然后我清除过滤器,下拉列表中的 selected 值就会消失:

根据 Kukeltje 添加输入文本的请求更新:

  1. Select下拉值

  1. 输入过滤器以便过滤掉所有行

  1. 清除过滤器,selected 值消失

我的支持 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;
        }
        
    }
}

我的小脸:

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

我把我的测试项目推送到github,如果你想测试它

git clone https://github.com/saycchai/jsf-test.git
cd jsf-test
chmod +x *.sh
./buildAndRun.sh

对于 payara 服务器: 浏览:http://localhost:8080/index.xhtml

对于其他服务器: http://localhost:8080/jsf-test/index.xhtml

最后我找到了如下解决方案:

我添加了一个阶段监听器,如果过滤器提交0行(作为请求参数传递,请参阅facelet中的onstart部分),然后将Dd值备份到APPLY_REQUEST_VALUES阶段之前的previousDd并且将备份值previousDd设置回RENDER_RESPONSE阶段前的Dd,代码如下:

支持 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;
        }

    }
}

小脸


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

相位监听器

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>