p:dataTable 过滤、排序、分页的结果显示在一行中

p:dataTable filtered, sorted, paginated results get displayed in a single row

我正在为我使用 PrimeFaces 6.2(社区版)制作的应用程序设计主题,我想让我的模拟 DAO object 在继续我的 css 模板化。

我过去遇到过一个问题,现在找不到正确的答案了。有人会指出我在代码中某处犯的错误吗?

详情:

我在 PrimeFaces Showcase 页面的帮助下使用 PrimeFaces LazyDataModel 制作了一个有点复杂的数据表。我的主要问题是,当我在过滤器字段中写一些东西或单击任何列 headers 进行数据排序甚至单击分页按钮时,我会遇到未扩展的数据呈现问题。

过滤、排序和分页的结果显示在单个连接行中,这是我不想要的。我在下面进一步发布了图片和代码以供深入了解。

另外,我想指出:

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>

懒惰模型:

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

查看 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;
    }

}

更新 1

我已将更新 属性 修改为 update="input-form-dt2:dlgDTOObjDetail" 以满足提供的建议。另外,我为对话框添加了 id 属性。问题依旧。

更新 2

我改变了方法,首先从基本的 DataTable 开始。此外,我已将 .xhtml 剥离到最低限度。它只包含一个带有 DataTable 的表单,如下所示:

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

如您所见,我还删除了所有事件侦听器。我在我的数据中添加了一个新字段 object(整数类型的 ID)并将 DataTables rowKey 绑定到它(以前绑定到字符串类型的用户 ID - 这不是一个好主意)。我的 DataTable 支持 bean 现在已经很基本了:

@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;
    }

}

现在,没有任何类型的自定义过滤器、排序器或分页器,甚至没有数据模型,只有简单列表中的原始数据 object。 点击分页器按钮或过滤数据时,输出结果与之前完全相同。所有结果数据仍显示在单个连接行中。

答案:

正如 Kukeltje 在评论中指出的那样,我在我的主容器中完全胡说八道,并向其中添加了一个自动更新组件。该组件搞砸了我的数据 table 事件,加载数据时没有 table 来保存它。一旦我从我的主容器中删除了组件,一切就都解决了。这是我的主容器的代码(注释掉了麻烦制造者)。

<div id="content-window">
    <p:outputPanel id="content-panel">
        <ui:include src="#{contentLoaderBean.mainContent}" />
        <!-- <p:autoUpdate /> -->
    </p:outputPanel>
</div>

我见过这种情况发生的唯一情况是,除了通过事件(页面或筛选器或排序或...)对数据表进行部分更新之外,还完全更新了数据表。在 rowSelect 中,您确实更新包含数据表的完整表格。这是不好的做法,如前所述,可能会导致您看起来如此删除它。

但是...在您的问题中,没有过滤器 ajax 事件显式更新完整数据表,因此不会导致它。然而,99% 的准确性有一些东西完全更新了数据表。三种选择

  • 您的实时代码中有些内容未包含在您问题的数据表中
  • 在您发布的代码之外还有其他东西造成了严重破坏(例如自动更新),
  • 服务器端的更新正在一个方法中完成