具有动态模型和列的 Primefaces 8.0 数据表
Primefaces 8.0 Datatable with Dynamic Model and Columns
我有一个 primefaces 数据表,它显示来自多个数据库 table 的数据。 select离子列表允许用户select特定数据库table显示。它按预期工作,除非使用 dataTable 过滤功能。例如,当来自 selection 列表的用户 selects 'DEPT' 时,将使用来自 DEPT table 的数据呈现 dataTable。用户可以 select 其他 table 正常。但是,如果用户 select 另一个 table 在过滤后调用 'EMP',则 dataTable 无法呈现,并出现以下异常:
javax.el.PropertyNotFoundException: The class 'example.dto.Dept' does not have the property 'firstName'.
at javax.el.BeanELResolver.getBeanProperty(BeanELResolver.java:576)
at javax.el.BeanELResolver.getValue(BeanELResolver.java:291)
at com.sun.faces.el.DemuxCompositeELResolver._getValue(DemuxCompositeELResolver.java:156)
这是 .xhtml 文件:
<h:form prependId="false">
<p:outputLabel for="selTbl" value="Select a table:" />
<p:autoComplete id="selTbl" value="#{mainBean.selectedTable}"
completeMethod="#{mainBean.filterAuditTables}" cache="true" dropdown="true" effect="fade"
minQueryLength="3" forceSelection="true" size="35" style="margin-left: 10px;">
<p:ajax event="itemSelect" listener="#{mainBean.onTableSelect}" process="@this" update="@form" />
</p:autoComplete>
<p:dataTable id="audTblData" value="#{mainBean.data}" var="row"
filteredValue="#{mainBean.filteredData}" resizableColumns="true" resizeMode="expand"
sortMode="multiple">
<f:facet name="header">
<h:outputText value="#{mainBean.selectedTable}" />
</f:facet>
<p:columns value="#{mainBean.tableColumns}" var="col" sortBy="#{row[col.property]}"
filterBy="#{row[col.property]}" filterMatchMode="contains">
<f:facet name="header">
<h:outputText value="#{col.header}" />
</f:facet>
<h:outputText value="#{row[col.property]}" />
</p:columns>
</p:dataTable>
</h:form>
MainBean.java:
@Named
@ViewScoped
public class MainBean implements Serializable {
private static final long serialVersionUID = 1L;
// these are used for the audited table selection list
private List<String> auditedTables;
private String selectedTable;
// these are used in the table that displays the audit data
private List<ColumnModel> tableColumns;
private List<Auditable> data;
private List<Auditable> filteredData;
@Inject
private AudviewService audviewService;
@PostConstruct
public void init() {
auditedTables = audviewService.getAuditedTables();
}
public List<String> filterAuditTables(String query) {
return auditedTables
.stream()
.filter(t -> t.contains(query.toUpperCase()))
.collect(Collectors.toList());
}
public void onTableSelect(SelectEvent<String> event) {
retrieveTableData();
}
public void retrieveTableData() {
List<String> columns = audviewService.listTableColumns(selectedTable);
// initialize columns for <p:dataTable>
tableColumns = new ArrayList<ColumnModel>();
for (String col : columns) {
tableColumns.add(new ColumnModel(col, AudviewUtil.columnToProperty(col)));
}
// retrieve data for the selected table
data = audviewService.getTableData(selectedTable);
}
/* getters and setters */
}
注意Auditable是Dept.java和Emp.java实现的接口。
如您所见,如果过滤或排序的列是不在新选择的 class 中的字段,则问题是从过滤或排序的数据表切换到另一个数据表。一个简单的解决方案是将所有需要的字段移动到 superclass Auditable
.
另一种方法是将数据表重置和更新分为两个步骤,这里是一个可能的解决方案(出于测试目的,我用静态代码替换了您的服务,因此在将我的解决方案适应您的代码时可能会出现一些错误):
xhtml
<h:form id="formTbl" prependId="false">
<p:outputLabel for="selTbl" value="Select a table:" />
<p:autoComplete id="selTbl" value="#{mainBean.selectedTable}"
completeMethod="#{mainBean.filterAuditTables}" cache="true"
dropdown="true" effect="fade" minQueryLength="3"
forceSelection="true" size="35" style="margin-left: 10px;">
<p:ajax event="itemSelect" listener="#{mainBean.onTableSelect}"
process="@this" onstart="PF('vtWidget').clearFilters()" />
</p:autoComplete>
<p:remoteCommand name="btn" process="@this" update="audTblData" />
<p:dataTable id="audTblData" value="#{mainBean.data}" var="row"
filteredValue="#{mainBean.filteredData}" resizableColumns="true"
resizeMode="expand" sortMode="multiple" widgetVar="vtWidget">
<f:facet name="header">
<h:outputText value="#{mainBean.selectedTable}" />
</f:facet>
<p:columns value="#{mainBean.tableColumns}" var="col"
sortBy="#{row[col.property]}" filterBy="#{row[col.property]}"
filterMatchMode="contains">
<f:facet name="header">
<h:outputText value="#{col.header}" />
</f:facet>
<h:outputText value="#{row[col.property]}" />
</p:columns>
</p:dataTable>
</h:form>
java
public void retrieveTableData() {
List<String> columns = listTableColumns(selectedTable);
// initialize columns for <p:dataTable>
tableColumns = new ArrayList<ColumnModel>();
for (String col : columns) {
tableColumns.add(new ColumnModel(col + " header", col));
}
// retrieve data for the selected table
data = getData(selectedTable);
DataTable dataTable = (DataTable) FacesContext.getCurrentInstance().getViewRoot().findComponent("formTbl:audTblData");
if (dataTable != null) {
dataTable.reset();
}
PrimeFaces.current().executeScript("btn()");
}
注意,如果你还需要管理分页。
我有一个 primefaces 数据表,它显示来自多个数据库 table 的数据。 select离子列表允许用户select特定数据库table显示。它按预期工作,除非使用 dataTable 过滤功能。例如,当来自 selection 列表的用户 selects 'DEPT' 时,将使用来自 DEPT table 的数据呈现 dataTable。用户可以 select 其他 table 正常。但是,如果用户 select 另一个 table 在过滤后调用 'EMP',则 dataTable 无法呈现,并出现以下异常:
javax.el.PropertyNotFoundException: The class 'example.dto.Dept' does not have the property 'firstName'.
at javax.el.BeanELResolver.getBeanProperty(BeanELResolver.java:576)
at javax.el.BeanELResolver.getValue(BeanELResolver.java:291)
at com.sun.faces.el.DemuxCompositeELResolver._getValue(DemuxCompositeELResolver.java:156)
这是 .xhtml 文件:
<h:form prependId="false">
<p:outputLabel for="selTbl" value="Select a table:" />
<p:autoComplete id="selTbl" value="#{mainBean.selectedTable}"
completeMethod="#{mainBean.filterAuditTables}" cache="true" dropdown="true" effect="fade"
minQueryLength="3" forceSelection="true" size="35" style="margin-left: 10px;">
<p:ajax event="itemSelect" listener="#{mainBean.onTableSelect}" process="@this" update="@form" />
</p:autoComplete>
<p:dataTable id="audTblData" value="#{mainBean.data}" var="row"
filteredValue="#{mainBean.filteredData}" resizableColumns="true" resizeMode="expand"
sortMode="multiple">
<f:facet name="header">
<h:outputText value="#{mainBean.selectedTable}" />
</f:facet>
<p:columns value="#{mainBean.tableColumns}" var="col" sortBy="#{row[col.property]}"
filterBy="#{row[col.property]}" filterMatchMode="contains">
<f:facet name="header">
<h:outputText value="#{col.header}" />
</f:facet>
<h:outputText value="#{row[col.property]}" />
</p:columns>
</p:dataTable>
</h:form>
MainBean.java:
@Named
@ViewScoped
public class MainBean implements Serializable {
private static final long serialVersionUID = 1L;
// these are used for the audited table selection list
private List<String> auditedTables;
private String selectedTable;
// these are used in the table that displays the audit data
private List<ColumnModel> tableColumns;
private List<Auditable> data;
private List<Auditable> filteredData;
@Inject
private AudviewService audviewService;
@PostConstruct
public void init() {
auditedTables = audviewService.getAuditedTables();
}
public List<String> filterAuditTables(String query) {
return auditedTables
.stream()
.filter(t -> t.contains(query.toUpperCase()))
.collect(Collectors.toList());
}
public void onTableSelect(SelectEvent<String> event) {
retrieveTableData();
}
public void retrieveTableData() {
List<String> columns = audviewService.listTableColumns(selectedTable);
// initialize columns for <p:dataTable>
tableColumns = new ArrayList<ColumnModel>();
for (String col : columns) {
tableColumns.add(new ColumnModel(col, AudviewUtil.columnToProperty(col)));
}
// retrieve data for the selected table
data = audviewService.getTableData(selectedTable);
}
/* getters and setters */
}
注意Auditable是Dept.java和Emp.java实现的接口。
如您所见,如果过滤或排序的列是不在新选择的 class 中的字段,则问题是从过滤或排序的数据表切换到另一个数据表。一个简单的解决方案是将所有需要的字段移动到 superclass Auditable
.
另一种方法是将数据表重置和更新分为两个步骤,这里是一个可能的解决方案(出于测试目的,我用静态代码替换了您的服务,因此在将我的解决方案适应您的代码时可能会出现一些错误):
xhtml
<h:form id="formTbl" prependId="false">
<p:outputLabel for="selTbl" value="Select a table:" />
<p:autoComplete id="selTbl" value="#{mainBean.selectedTable}"
completeMethod="#{mainBean.filterAuditTables}" cache="true"
dropdown="true" effect="fade" minQueryLength="3"
forceSelection="true" size="35" style="margin-left: 10px;">
<p:ajax event="itemSelect" listener="#{mainBean.onTableSelect}"
process="@this" onstart="PF('vtWidget').clearFilters()" />
</p:autoComplete>
<p:remoteCommand name="btn" process="@this" update="audTblData" />
<p:dataTable id="audTblData" value="#{mainBean.data}" var="row"
filteredValue="#{mainBean.filteredData}" resizableColumns="true"
resizeMode="expand" sortMode="multiple" widgetVar="vtWidget">
<f:facet name="header">
<h:outputText value="#{mainBean.selectedTable}" />
</f:facet>
<p:columns value="#{mainBean.tableColumns}" var="col"
sortBy="#{row[col.property]}" filterBy="#{row[col.property]}"
filterMatchMode="contains">
<f:facet name="header">
<h:outputText value="#{col.header}" />
</f:facet>
<h:outputText value="#{row[col.property]}" />
</p:columns>
</p:dataTable>
</h:form>
java
public void retrieveTableData() {
List<String> columns = listTableColumns(selectedTable);
// initialize columns for <p:dataTable>
tableColumns = new ArrayList<ColumnModel>();
for (String col : columns) {
tableColumns.add(new ColumnModel(col + " header", col));
}
// retrieve data for the selected table
data = getData(selectedTable);
DataTable dataTable = (DataTable) FacesContext.getCurrentInstance().getViewRoot().findComponent("formTbl:audTblData");
if (dataTable != null) {
dataTable.reset();
}
PrimeFaces.current().executeScript("btn()");
}
注意,如果你还需要管理分页。