带有列切换器的 Primefaces 数据表与列上的排序功能冲突
Primefaces datatable with column toggler conflicts with sort function on column
我有一个数据 table,它有一个列切换器。当我取消选中一列并对字段进行排序时,table 是错误的。未选中字段的 header 弹出,所有数据向左移动,留下 1 列为空。
我的 table.xhtml 文件:
<h:form>
<p:dataTable id="registrations" var="registration" tableStyle="table-layout: auto;" widgetVar="registrationsTable"
value="#{overviewBean.listOfRegistrations}"
filteredValue="#{overviewBean.filteredListOfRegistrations}" emptyMessage="No registrations found with given criteria" >
<f:facet name="header">
<p:outputPanel style="text-align:left;">
<h:outputText value="Search all fields: " />
<p:inputText id="globalFilter" onkeyup="PF('registrationsTable').filter()" style="width:150px;" placeholder="Enter keyword"/>
<p:commandButton id="toggler" type="button" value="Columns" icon="ui-icon-calculator" style="float:right;"/>
<p:columnToggler datasource="registrations" trigger="toggler" >
</p:columnToggler>
</p:outputPanel>
</f:facet>
<p:column headerText="Active" visible="false">
<h:outputText value="Y" />
</p:column>
<p:column headerText="Firstname" filterBy="#{registration.firstname}" filterStyle="display:none" sortBy="#{registration.firstname}">
<h:outputText value="#{registration.firstname}" />
</p:column>
<p:column headerText="Lastname" filterBy="#{registration.lastname}" filterStyle="display:none" sortBy="#{registration.lastname}">
<h:outputText value="#{registration.lastname}" />
</p:column>
</p:dataTable>
</h:form>
我在这个博客中找到了答案 post:
http://blog.primefaces.org/?p=3341
解决方案是保持支持 bean 中所有列的 Visibility
状态。
切换器必须在您的支持 bean 中触发 onToggle 函数:
<p:columnToggler datasource="registrations" trigger="toggler" >
<p:ajax event="toggle" listener="#{overviewBean.onToggle}" />
</p:columnToggler>
每一列都必须由支持 bean 中的布尔列表设置:
<p:column headerText="Entry date" sortBy="#{registration.entryDate}" visible="#{overviewBean.list[0]}">
<h:outputText value="#{registration.entryDate}">
<f:convertDateTime pattern="dd/MM/yyyy" />
</h:outputText>
</p:column>
在支持 bean 中,您必须有一个表示每个字段可见性的布尔值列表:
private List<Boolean> list;
public List<Boolean> getList() {
return list;
}
public void setList(List<Boolean> list) {
this.list = list;
}
public void onToggle(ToggleEvent e) {
list.set((Integer) e.getData(), e.getVisibility() == Visibility.VISIBLE);
}
在 @PostConstruct
方法中,您必须初始化这个 bean 列表:
list = Arrays.asList(false, true, true);
在 Primefaces 6.1.10 中,您可以在数据表上使用 multiViewState="true",连同在 columnToggler 中调用 ajax,您可以在没有支持 bean 解决方法的情况下保存列可见性。在我的测试中,列上的排序功能不再有冲突。
更新:在使用 multiViewState="true" 进行更多测试后,在打开或关闭列后对结果进行排序时似乎仍然存在一些错误。切换到另一个页面并返回会导致正确呈现 table 和列顺序。创建问题 https://github.com/primefaces/primefaces/issues/3088
增强功能在以下方面实施:
https://github.com/primefaces/primefaces/issues/3044
用法是:
<h:form id="form">
<p:dataTable id="cars" var="car" value="#{dtBasicView.cars}" multiViewState="true">
<f:facet name="header">
List of Cars
<p:commandButton id="toggler" type="button" value="Columns" style="float:right" icon="ui-icon-calculator" />
<p:columnToggler datasource="cars" trigger="toggler">
<p:ajax event="toggle" />
</p:columnToggler>
</f:facet>
<p:column headerText="Id" sortBy="#{car.id}"><h:outputText value="#{car.id}" /></p:column>
<p:column headerText="Year" sortBy="#{car.year}"><h:outputText value="#{car.year}" /></p:column>
<p:column headerText="Brand" sortBy="#{car.brand}" ><h:outputText value="#{car.brand}" /></p:column>
</p:dataTable>
</h:form>
有关 PrimeFaces 中 multiViewState 的更多信息,请参阅 TableState 演示:
https://www.primefaces.org/showcase/ui/data/datatable/tableState.xhtml
这也包括存储排序的可能性。
新的列重新排序功能:
https://github.com/primefaces/primefaces/issues/3041
新的列调整大小功能:
https://github.com/primefaces/primefaces/issues/3046
替代解决方案:通过子类化 PrimeFaces p:columnToggler
.
创建您自己的有状态切换器
更新: 补偿了列可能将其 rendered
属性设置为 false
.
的事实
package my.domain;
import org.omnifaces.util.Components;
import org.primefaces.behavior.ajax.AjaxBehavior;
import org.primefaces.behavior.ajax.AjaxBehaviorListenerImpl;
import org.primefaces.component.column.Column;
import org.primefaces.component.columntoggler.ColumnToggler;
import org.primefaces.component.datatable.DataTable;
import org.primefaces.event.ToggleEvent;
import javax.el.MethodExpression;
import javax.faces.component.FacesComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ComponentSystemEvent;
import javax.faces.event.ListenerFor;
import javax.faces.event.PostAddToViewEvent;
import java.io.IOException;
@FacesComponent(
value = StatefulColumnToggler.COMPONENT_TYPE,
createTag = true,
tagName = StatefulColumnToggler.TAG_NAME,
namespace = StatefulColumnToggler.NAMESPACE
)
@ListenerFor(systemEventClass=PostAddToViewEvent.class)
public class StatefulColumnToggler extends ColumnToggler {
public static final String TAG_NAME = "statefulColumnToggler";
public static final String COMPONENT_TYPE = "my.domain.statefulColumnToggler";
public static final String NAMESPACE = "http://xmlns.domain.my/jsf/component";
public void processEvent(ComponentSystemEvent event) throws AbortProcessingException {
super.processEvent(event);
if (event instanceof PostAddToViewEvent) {
// This behaviour is static, so it does not have to be in the partial state: recreate it on every request.
AjaxBehavior ajaxBehavior = (AjaxBehavior) getFacesContext().getApplication().createBehavior(AjaxBehavior.BEHAVIOR_ID);
MethodExpression methodExpression = Components.createMethodExpression("#{component.onToggleHandler}", null, new Class<?>[]{ToggleEvent.class});
ajaxBehavior.addAjaxBehaviorListener(new AjaxBehaviorListenerImpl(methodExpression, methodExpression));
addClientBehavior("toggle", ajaxBehavior);
}
}
public void onToggleHandler(ToggleEvent toggleEvent) {
final DataTable dataTable = (DataTable) this.getDataSourceComponent();
final int clientColumnIndex = (Integer) toggleEvent.getData();
// Compensate for columns that are on the server, but not in the client.
int columnsNotInClient = 0;
for (int serverColumnIndex = 0; ; serverColumnIndex++) {
if (serverColumnIndex >= dataTable.getColumns().size()) {
throw new IllegalArgumentException("received index " + clientColumnIndex + " from client, but there is no rendered server side column with that index");
}
Column column = (Column) dataTable.getColumns().get(serverColumnIndex);
if (!column.isRendered()) {
columnsNotInClient++;
}
if (serverColumnIndex - columnsNotInClient == clientColumnIndex) {
column.setVisible(!column.isVisible());
// Must delete any 'user' specified value expression, or it will take precedence over the toggled value.
column.setValueExpression(Column.PropertyKeys.visible.name(), null);
break;
}
}
}
}
声明命名空间
xmlns:my=http://xmlns.domain.my/jsf/component
并将其添加到页面。
<my:statefulColumnToggler datasource="table_id" trigger="button_id" />
我有一个数据 table,它有一个列切换器。当我取消选中一列并对字段进行排序时,table 是错误的。未选中字段的 header 弹出,所有数据向左移动,留下 1 列为空。
我的 table.xhtml 文件:
<h:form>
<p:dataTable id="registrations" var="registration" tableStyle="table-layout: auto;" widgetVar="registrationsTable"
value="#{overviewBean.listOfRegistrations}"
filteredValue="#{overviewBean.filteredListOfRegistrations}" emptyMessage="No registrations found with given criteria" >
<f:facet name="header">
<p:outputPanel style="text-align:left;">
<h:outputText value="Search all fields: " />
<p:inputText id="globalFilter" onkeyup="PF('registrationsTable').filter()" style="width:150px;" placeholder="Enter keyword"/>
<p:commandButton id="toggler" type="button" value="Columns" icon="ui-icon-calculator" style="float:right;"/>
<p:columnToggler datasource="registrations" trigger="toggler" >
</p:columnToggler>
</p:outputPanel>
</f:facet>
<p:column headerText="Active" visible="false">
<h:outputText value="Y" />
</p:column>
<p:column headerText="Firstname" filterBy="#{registration.firstname}" filterStyle="display:none" sortBy="#{registration.firstname}">
<h:outputText value="#{registration.firstname}" />
</p:column>
<p:column headerText="Lastname" filterBy="#{registration.lastname}" filterStyle="display:none" sortBy="#{registration.lastname}">
<h:outputText value="#{registration.lastname}" />
</p:column>
</p:dataTable>
</h:form>
我在这个博客中找到了答案 post: http://blog.primefaces.org/?p=3341
解决方案是保持支持 bean 中所有列的 Visibility
状态。
切换器必须在您的支持 bean 中触发 onToggle 函数:
<p:columnToggler datasource="registrations" trigger="toggler" >
<p:ajax event="toggle" listener="#{overviewBean.onToggle}" />
</p:columnToggler>
每一列都必须由支持 bean 中的布尔列表设置:
<p:column headerText="Entry date" sortBy="#{registration.entryDate}" visible="#{overviewBean.list[0]}">
<h:outputText value="#{registration.entryDate}">
<f:convertDateTime pattern="dd/MM/yyyy" />
</h:outputText>
</p:column>
在支持 bean 中,您必须有一个表示每个字段可见性的布尔值列表:
private List<Boolean> list;
public List<Boolean> getList() {
return list;
}
public void setList(List<Boolean> list) {
this.list = list;
}
public void onToggle(ToggleEvent e) {
list.set((Integer) e.getData(), e.getVisibility() == Visibility.VISIBLE);
}
在 @PostConstruct
方法中,您必须初始化这个 bean 列表:
list = Arrays.asList(false, true, true);
在 Primefaces 6.1.10 中,您可以在数据表上使用 multiViewState="true",连同在 columnToggler 中调用 ajax,您可以在没有支持 bean 解决方法的情况下保存列可见性。在我的测试中,列上的排序功能不再有冲突。
更新:在使用 multiViewState="true" 进行更多测试后,在打开或关闭列后对结果进行排序时似乎仍然存在一些错误。切换到另一个页面并返回会导致正确呈现 table 和列顺序。创建问题 https://github.com/primefaces/primefaces/issues/3088
增强功能在以下方面实施: https://github.com/primefaces/primefaces/issues/3044
用法是:
<h:form id="form">
<p:dataTable id="cars" var="car" value="#{dtBasicView.cars}" multiViewState="true">
<f:facet name="header">
List of Cars
<p:commandButton id="toggler" type="button" value="Columns" style="float:right" icon="ui-icon-calculator" />
<p:columnToggler datasource="cars" trigger="toggler">
<p:ajax event="toggle" />
</p:columnToggler>
</f:facet>
<p:column headerText="Id" sortBy="#{car.id}"><h:outputText value="#{car.id}" /></p:column>
<p:column headerText="Year" sortBy="#{car.year}"><h:outputText value="#{car.year}" /></p:column>
<p:column headerText="Brand" sortBy="#{car.brand}" ><h:outputText value="#{car.brand}" /></p:column>
</p:dataTable>
</h:form>
有关 PrimeFaces 中 multiViewState 的更多信息,请参阅 TableState 演示: https://www.primefaces.org/showcase/ui/data/datatable/tableState.xhtml
这也包括存储排序的可能性。
新的列重新排序功能: https://github.com/primefaces/primefaces/issues/3041
新的列调整大小功能: https://github.com/primefaces/primefaces/issues/3046
替代解决方案:通过子类化 PrimeFaces p:columnToggler
.
更新: 补偿了列可能将其 rendered
属性设置为 false
.
package my.domain;
import org.omnifaces.util.Components;
import org.primefaces.behavior.ajax.AjaxBehavior;
import org.primefaces.behavior.ajax.AjaxBehaviorListenerImpl;
import org.primefaces.component.column.Column;
import org.primefaces.component.columntoggler.ColumnToggler;
import org.primefaces.component.datatable.DataTable;
import org.primefaces.event.ToggleEvent;
import javax.el.MethodExpression;
import javax.faces.component.FacesComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ComponentSystemEvent;
import javax.faces.event.ListenerFor;
import javax.faces.event.PostAddToViewEvent;
import java.io.IOException;
@FacesComponent(
value = StatefulColumnToggler.COMPONENT_TYPE,
createTag = true,
tagName = StatefulColumnToggler.TAG_NAME,
namespace = StatefulColumnToggler.NAMESPACE
)
@ListenerFor(systemEventClass=PostAddToViewEvent.class)
public class StatefulColumnToggler extends ColumnToggler {
public static final String TAG_NAME = "statefulColumnToggler";
public static final String COMPONENT_TYPE = "my.domain.statefulColumnToggler";
public static final String NAMESPACE = "http://xmlns.domain.my/jsf/component";
public void processEvent(ComponentSystemEvent event) throws AbortProcessingException {
super.processEvent(event);
if (event instanceof PostAddToViewEvent) {
// This behaviour is static, so it does not have to be in the partial state: recreate it on every request.
AjaxBehavior ajaxBehavior = (AjaxBehavior) getFacesContext().getApplication().createBehavior(AjaxBehavior.BEHAVIOR_ID);
MethodExpression methodExpression = Components.createMethodExpression("#{component.onToggleHandler}", null, new Class<?>[]{ToggleEvent.class});
ajaxBehavior.addAjaxBehaviorListener(new AjaxBehaviorListenerImpl(methodExpression, methodExpression));
addClientBehavior("toggle", ajaxBehavior);
}
}
public void onToggleHandler(ToggleEvent toggleEvent) {
final DataTable dataTable = (DataTable) this.getDataSourceComponent();
final int clientColumnIndex = (Integer) toggleEvent.getData();
// Compensate for columns that are on the server, but not in the client.
int columnsNotInClient = 0;
for (int serverColumnIndex = 0; ; serverColumnIndex++) {
if (serverColumnIndex >= dataTable.getColumns().size()) {
throw new IllegalArgumentException("received index " + clientColumnIndex + " from client, but there is no rendered server side column with that index");
}
Column column = (Column) dataTable.getColumns().get(serverColumnIndex);
if (!column.isRendered()) {
columnsNotInClient++;
}
if (serverColumnIndex - columnsNotInClient == clientColumnIndex) {
column.setVisible(!column.isVisible());
// Must delete any 'user' specified value expression, or it will take precedence over the toggled value.
column.setValueExpression(Column.PropertyKeys.visible.name(), null);
break;
}
}
}
}
声明命名空间
xmlns:my=http://xmlns.domain.my/jsf/component
并将其添加到页面。
<my:statefulColumnToggler datasource="table_id" trigger="button_id" />