p:datatable在ajax刷新后丢失排序列和顺序

时间:2022-09-17 10:50:28

I have a button on a page that causes my data table to refresh via an AJAX request. Something like this:

我在页面上有一个按钮,通过AJAX请求刷新我的数据表。像这样的东西:

<h:form id="datatable">
<p:dataTable/>
</h:form>
<p:commandButton update=":datatable">

This is all fine an dandy except that when the table is refreshed it reverts to not sorting anything while still showing that it's sorting based on the previous value. In other words, the header is still highlighted and the arrow is still pointing in the sort direction but no sort is actually being performed. Obviously this isn't ideal.

这一切都很好,但是当表刷新时,它会恢复到不排序任何东西,同时仍然显示它是基于前一个值进行排序。换句话说,标题仍然突出显示,箭头仍然指向排序方向,但实际上没有进行排序。显然这并不理想。

Ideally I'd like the component to keep it's sort order in the view state and then submit the proper parameters during the AJAX request (so that the sort is correctly defined). Am I missing a parameter or something? Does anyone else have this issue?

理想情况下,我希望组件在视图状态中保持其排序顺序,然后在AJAX请求期间提交适当的参数(以便正确定义排序)。我错过了一个参数或什么?还有其他人有这个问题吗?

From what I can tell when the table is expecting a sort back it posts the following options:

从表格预期排序时我可以看出它发布了以下选项:

<componentID>_sortDir
<componentID>_sortKey
<componentID>_sorting
<componentID>_updateBody

When I refresh the form this doesn't happen. It also doesn't happen if I just refresh the table (thought I could work around things by updating the component directly). Is there a way to get the table to refresh correctly?

当我刷新表单时,这不会发生。如果我只刷新表格也不会发生这种情况(我想通过直接更新组件可以解决问题)。有没有办法让表格正确刷新?

8 个解决方案

#1


10  

I wrote an extension to @truemmer's solution. His reverts the sorting order back to the default, where as mine will reverts to the previous sort the user selected.

我为@ truemmer的解决方案写了一个扩展。他将排序顺序恢复为默认值,然后我将恢复到用户选择的上一个排序。

function postAjaxSortTable(datatable)
{
    var selectedColumn = datatable.jq.find('.ui-state-active');
    if(selectedColumn.length <= 0)
    {
        return;
    }
    var sortorder = "ASCENDING";
    if(selectedColumn.find('.ui-icon-triangle-1-s').length > 0)
    {
        sortorder = "DESCENDING";
    }
    datatable.sort(selectedColumn, sortorder);
}

Updating the same table as truemmer's works like this:

像truemmer一样更新相同的表是这样的:

<p:commandButton value="refresh" action="#{tableController.refreshPrices}" update="myTable" oncomplete="postAjaxSortTable(myTableWidget)" />

EDIT: Primefaces 4.0 MultiSort support

编辑:Primefaces 4.0 MultiSort支持

function postAjaxSortTable(datatable) {
    var selectedColumn = undefined;

    // multisort support
    if(datatable && datatable.cfg.multiSort) {
        if(datatable.sortMeta.length > 0) {
            var lastSort = datatable.sortMeta[datatable.sortMeta.length-1];
            selectedColumn = $(document.getElementById(lastSort.col));
        }
    } else {
        selectedColumn = datatable.jq.find('.ui-state-active');
    }

    // no sorting selected -> quit
    if(!selectedColumn || selectedColumn.length <= 0) {
        return;
    }

    var sortorder = selectedColumn.data('sortorder')||"DESCENDING";
    datatable.sort(selectedColumn, sortorder, datatable.cfg.multiSort);
}

#2


5  

First of all, there is an open ticket on the PrimeFaces Issue Tracker, which targets this question but was recently labeled as "won't fix".

首先,PrimeFaces问题跟踪器上有一张开放票据,它针对这个问题,但最近标记为“无法修复”。

Nevertheless, I found a nice workaround. The sorting of PrimeFaces tables can be triggered by calling an undocumented JavaScript method on the datatable's widget. The following might not work for future releases of PrimeFaces, but it does for 3.4.2:

不过,我找到了一个很好的解决方法。可以通过在数据表的窗口小部件上调用未记录的JavaScript方法来触发PrimeFaces表的排序。以下可能不适用于PrimeFaces的未来版本,但它适用于3.4.2:

Just add the following to your component, which triggers the update:

只需将以下内容添加到组件中即可触发更新:

oncomplete="myTableWidget.sort($(PrimeFaces.escapeClientId('#{p:component('sortColumnId')}')), 'ASCENDING')"

The table might look like this:

该表可能如下所示:

<p:dataTable id="myTable"
             widgetVar="myTableWidget"
             value="#{myArticles}"
             var="article"
             sortBy="#{article.price}"
             sortOrder="ASCENDING">
    ...

    <p:column id="price" sortBy="#{article.price}">
        <f:facet name="header">
            <h:outputText value="Price" />
        </f:facet>
        <h:outputText value="#{article.price}" />
    </p:column>

</p:dataTable>

So updating the table could work like this.

所以更新表可以这样工作。

<p:commandButton value="refresh" action="#{tableController.refreshPrices}" update="myTable" oncomplete="myTableWidget.sort($(PrimeFaces.escapeClientId('#{p:component('price')}')), 'ASCENDING')" />

#3


3  

EDIT:

编辑:

Solution posted below (LazyTable) works for the p:dataTable backed with LazyDataModel. Overriden load method is called after every update/refresh on the desired table and it handles sort properly. The problem with simple p:dataTable is that it performs predefined sort only on the first load, or after the click on sort column. This is a normal behaviour of a simple table.

下面发布的解决方案(LazyTable)适用于使用LazyDataModel支持的p:dataTable。在所需的表上每次更新/刷新后调用Overriden load方法,它会正确处理排序。简单的p:dataTable的问题在于它仅在第一次加载时或在单击sort列之后执行预定义排序。这是简单表的正常行为。

So what are your possibilities for simple table :

那么简单表的可能性是什么:

  • Don't sort the table after update, but remove the sort column so end user is not missinformed. Add this to your action listener or action method for your update button :

    更新后不要对表进行排序,但删除排序列以便最终用户不会错过信息。将此添加到您的更新按钮的动作侦听器或操作方法:

    UIComponent table  = FacesContext.getCurrentInstance().getViewRoot().findComponent(":dataTablesForm:dataTableId");
    table.setValueExpression("sortBy", null);
    
  • Update the sort of the table manually by script. This is not the best solution, but primefaces doesn't provide any client side function for "resorting" the table. Basically you know that only one column at a time can be sorted and this column has a .ui-state-active. You can use it in a script and trigger 2 clicks on that column (1. click - other sort order, 2. click - back to current sort order).

    通过脚本手动更新表的种类。这不是最好的解决方案,但是primefaces不提供“求助”表的任何客户端功能。基本上,您知道一次只能对一列进行排序,并且此列具有.ui-state-active。您可以在脚本中使用它并触发该列的2次单击(1.单击 - 其他排序顺序,2。单击 - 返回当前排序顺序)。

     <h:form id="mainForm">  
    <div id="tableDiv">
       <p:dataTable id="dataTable" var="item" value="#{testBean.dummyItems}">
          .
          .
          .
       </p:dataTable> 
       <p:commandButton value="Refresh" oncomplete="refreshSort();" update=":mainForm:dataTable"/>
     </div>
    

    And script function :

    和脚本功能:

    function refreshSort(){
    jQuery('#tableDiv').find('.ui-state-active').trigger('click');
    jQuery('#tableDiv').find('.ui-state-active').trigger('click');
    }
    

I know this is not the best workaround, but it works. You can use it as an inspiration to make something better.

我知道这不是最好的解决方法,但它确实有效。你可以用它作为灵感来创造更好的东西。

LazyTable

IMHO the most proper way is to update directly the component you want. So for example :

恕我直言,最恰当的方法是直接更新你想要的组件。例如:

<h:form id="dataTableForm">
  <p:dataTable id="dataTableToUpdate">
    .
    .
    .
  </p:dataTable>
  <p:commandButton value="Refresh" update=":dataTableForm:dataTableToUpdate" />
</h:form>

It should work fine in this scenario (I suppose it is your purpose) : Open the .xhtml with your p:dataTable, sort some column (keep the facelet opened), update dataTables data from another .xhtml for example, click on refresh button. The dataTable should show your added value in correct (previously chosen) sort order - sorting was performed after update.

它应该在这种情况下工作正常(我想这是你的目的):用你的p:dataTable打开.xhtml,排序一些列(保持facelet打开),从另一个.xhtml更新dataTables数据,例如,点击刷新按钮。 dataTable应以正确(先前选择的)排序顺序显示您的添加值 - 更新后执行排序。

Hope it helped !

希望它有所帮助!

#4


1  

Add this PhaseListener to your application and both sorting and filtering will be kept after updating the DataTable.

将此PhaseListener添加到您的应用程序中,更新DataTable后将保留排序和过滤。

public class DataTableUpdatePhaseListener implements PhaseListener {

    private static final long serialVersionUID = 1L;

    @Override
    public void afterPhase(PhaseEvent event) {
        // Nothing to do here
    }

    @Override
    public void beforePhase(PhaseEvent event) {
        FacesContext facesContext = event.getFacesContext();
        if (!facesContext.isPostback()) {
            return;
        }

        PartialViewContext partialViewContext = facesContext.getPartialViewContext();
        if (partialViewContext != null) {
            Collection<String> renderIds = partialViewContext.getRenderIds();

            for (String renderId : renderIds) {
                UIComponent component = facesContext.getViewRoot().findComponent(renderId);
                if (component instanceof DataTable) {
                    DataTable table = (DataTable) component;
                    if (!table.isLazy()) {
                        updateDataTable(table);
                    }
                }
            }
        }
    }

    @Override
    public PhaseId getPhaseId() {
        return PhaseId.RENDER_RESPONSE;
    }

    private void updateDataTable(DataTable table) {
        FacesContext facesContext = FacesContext.getCurrentInstance();
        if (facesContext == null || table == null) {
            return;
        }

        // Reapply filtering
        if (!table.getFilters().isEmpty()) {
            FilterFeature filterFeature = new FilterFeature();
            filterFeature.decode(facesContext, table);
        } else {
            table.setFilteredValue(null);
        }

        // Reapply sorting
        ValueExpression tableSortByVE = table.getValueExpression("sortBy");
        if (tableSortByVE != null) {
            String tableSortByExpression = tableSortByVE.getExpressionString();

            // Loop on children, that are the columns, to find the one whose order must be applied.
            for (UIComponent child : table.getChildren()) {
                Column column = (Column) child;
                ValueExpression columnSortByVe = column.getValueExpression("sortBy");
                if (columnSortByVe != null) {
                    String columnSortByExpression = columnSortByVe.getExpressionString();

                    if (tableSortByExpression != null && tableSortByExpression.equals(columnSortByExpression)) {
                        // Now sort table content
                        SortFeature sortFeature = new SortFeature();
                        sortFeature.sort(facesContext, table, tableSortByVE,
                                SortOrder.valueOf(table.getSortOrder().toUpperCase(Locale.ENGLISH)),
                                table.getSortFunction());

                        break;
                    }
                }
            }
        }
    }
}

This is for non-lazy data tables. Data tables using a lazy model do not require this, as the lazy model will take care of sorting and filtering. For non-lazy data tables, this should work with both single and multiple column sorting but there is a bug in Primefaces that makes DataTable loose its MultiSortMeta between postbacks when updating the table. This means that the columns selected for sorting before postback are completely lost from FacesContext and component state and cannot be retrieved anymore upon postback. I'll look more into this later and provide an update if I manage to find a workaround or maybe Primefaces will fix it soon.

这适用于非惰性数据表。使用惰性模型的数据表不需要这样,因为惰性模型将负责排序和过滤。对于非惰性数据表,这应该适用于单列和多列排序,但Primefaces中存在一个错误,使得DataTable在更新表时在回发之间松散其MultiSortMeta。这意味着在回发之前选择用于排序的列从FacesContext和组件状态完全丢失,并且在回发时不再检索。如果我设法找到一个解决方法,或者Primefaces很快就会修复它,我会稍后再详细介绍并提供更新。

#5


0  

I implement a Comparator with my toString() method

我用toString()方法实现了一个Comparator

municipios = municipioFacade.findAll();
Collections.sort(municipios, new DefaultComparator());
UIComponent table = FacesContext.getCurrentInstance().getViewRoot().findComponent(":municipios:municipios-tabla");
    table.setValueExpression("sortBy", null);

comparator

比较

public class DefaultComparator implements Comparator {

    @Override
    public int compare(Object o1, Object o2) {
        return o1.toString().compareToIgnoreCase(o2.toString());
    }
}

#6


0  

The solution is to have a conversational bean. In this case the p:dataTable refreshes the table and its entries without affecting the sort order. In the case when you have a p:commandButton on each line the table refresh works correctly too.

解决方案是拥有一个会话bean。在这种情况下,p:dataTable刷新表及其条目而不影响排序顺序。如果每行都有一个p:commandButton,表刷新也能正常工作。

The conversational bean:

会话bean:

@Named
@Stateful
@ConversationScoped
public class ItemBean {

    @Inject
    private Conversation conversation;

    private List<Item> items;

    public List<Item> getItems() {
        return this.items;
    }

    public void retrieve() {
        // if it's an Ajax call then avoid retrieving items
        if (FacesContext.getCurrentInstance().isPostback()) {
            return;
        }
        // start a conversation
        if (this.conversation.isTransient()) {
            this.conversation.begin();
        }
        this.items = retrieveItems();
    }
}

The associated page:

关联页面:

<f:metadata>
    <f:event type="preRenderView" listener="#{itemBean.retrieve}" />
</f:metadata>

<h:form id="form">
    <p:dataTable id="table" var="_item" value="#{testBean.items}">
    ... <!-- you can have the p:commandButton in a column too, to refresh the respective column only
        <p:commandButton value="Refresh" action="#{itemBean.update(_item)}" 
        -->
    </p:dataTable> 
    <p:commandButton value="Refresh" action="#{itemBean.update}" update=":form:table"/>
</h:form>

#7


0  

I had the same problem as you. I was able to fix the issue using a LazyDataModel. Because of the PrimeFaces issue with the row index, I needed to add the extra UtilitiesLazyDataModel(see row index comments):

我遇到了和你一样的问题。我能够使用LazyDataModel解决问题。由于PrimeFaces问题与行索引,我需要添加额外的UtilitiesLazyDataModel(请参阅行索引注释):

public abstract class UtilitiesLazyDataModel <T> extends LazyDataModel<T>{

    public UtilitiesLazyDataModel() {
    }    

    @Override
    public void setRowIndex(int rowIndex) {
        /*
         * The following is in ancestor (LazyDataModel):
         * this.rowIndex = rowIndex == -1 ? rowIndex : (rowIndex % pageSize);
         */
        if (rowIndex == -1 || getPageSize() == 0) {
            super.setRowIndex(-1);
        } else {
            super.setRowIndex(rowIndex % getPageSize());
        }      
    }
}

Then use your LazyDataModel class with this:

然后使用您的LazyDataModel类:

public class MyDataModel extends UtilitiesLazyDataModel<MyObjectClass>{

//override getRowData and getRowKey

}

#8


0  

@Rares Oltean's approach could also be implemented using preRenderView event listener. On your jsf page register listener:

@Rares Oltean的方法也可以使用preRenderView事件监听器来实现。在你的jsf页面注册监听器:

<f:event listener="#{managedBean.preRenderViewListener}" type="preRenderView" />

and implement it in a ManagedBean:

并在ManagedBean中实现它:

public void preRenderViewListener() {
    FacesContext facesContext = FacesContext.getCurrentInstance();
    if (!facesContext.isPostback()) {
        return;
    }

    PartialViewContext partialViewContext = facesContext.getPartialViewContext();
    if (partialViewContext != null) {
        Collection<String> renderIds = partialViewContext.getRenderIds();

        for (String renderId : renderIds) {
            UIComponent component = facesContext.getViewRoot().findComponent(renderId);
            if (component instanceof DataTable) {
                DataTable table = (DataTable) component;
                if (!table.isLazy()) {
                    updateDataTable(table);
                }
            }
        }
    }
}

private void updateDataTable(DataTable table) {
    FacesContext facesContext = FacesContext.getCurrentInstance();
    if (facesContext == null || table == null) {
        return;
    }
    if (!table.getFilters().isEmpty()) {
        FilterFeature filterFeature = new FilterFeature();
        filterFeature.decode(facesContext, table);
    } else {
        table.setFilteredValue(null);
    }
    ValueExpression tableSortByVE = table.getValueExpression("sortBy");
    if (tableSortByVE != null) {
        String tableSortByExpression = tableSortByVE.getExpressionString();
        for (UIComponent child : table.getChildren()) {
            Column column = (Column) child;
            ValueExpression columnSortByVe = column.getValueExpression("sortBy");
            if (columnSortByVe != null) {
                String columnSortByExpression = columnSortByVe.getExpressionString();
                if (tableSortByExpression != null && tableSortByExpression.equals(columnSortByExpression)) {
                    SortFeature sortFeature = new SortFeature();
                    sortFeature.sort(facesContext, table, tableSortByVE, table.getVar(),
                            SortOrder.valueOf(table.getSortOrder().toUpperCase(Locale.ENGLISH)),
                            table.getSortFunction());
                    break;
                }
            }
        }
    }
}

#1


10  

I wrote an extension to @truemmer's solution. His reverts the sorting order back to the default, where as mine will reverts to the previous sort the user selected.

我为@ truemmer的解决方案写了一个扩展。他将排序顺序恢复为默认值,然后我将恢复到用户选择的上一个排序。

function postAjaxSortTable(datatable)
{
    var selectedColumn = datatable.jq.find('.ui-state-active');
    if(selectedColumn.length <= 0)
    {
        return;
    }
    var sortorder = "ASCENDING";
    if(selectedColumn.find('.ui-icon-triangle-1-s').length > 0)
    {
        sortorder = "DESCENDING";
    }
    datatable.sort(selectedColumn, sortorder);
}

Updating the same table as truemmer's works like this:

像truemmer一样更新相同的表是这样的:

<p:commandButton value="refresh" action="#{tableController.refreshPrices}" update="myTable" oncomplete="postAjaxSortTable(myTableWidget)" />

EDIT: Primefaces 4.0 MultiSort support

编辑:Primefaces 4.0 MultiSort支持

function postAjaxSortTable(datatable) {
    var selectedColumn = undefined;

    // multisort support
    if(datatable && datatable.cfg.multiSort) {
        if(datatable.sortMeta.length > 0) {
            var lastSort = datatable.sortMeta[datatable.sortMeta.length-1];
            selectedColumn = $(document.getElementById(lastSort.col));
        }
    } else {
        selectedColumn = datatable.jq.find('.ui-state-active');
    }

    // no sorting selected -> quit
    if(!selectedColumn || selectedColumn.length <= 0) {
        return;
    }

    var sortorder = selectedColumn.data('sortorder')||"DESCENDING";
    datatable.sort(selectedColumn, sortorder, datatable.cfg.multiSort);
}

#2


5  

First of all, there is an open ticket on the PrimeFaces Issue Tracker, which targets this question but was recently labeled as "won't fix".

首先,PrimeFaces问题跟踪器上有一张开放票据,它针对这个问题,但最近标记为“无法修复”。

Nevertheless, I found a nice workaround. The sorting of PrimeFaces tables can be triggered by calling an undocumented JavaScript method on the datatable's widget. The following might not work for future releases of PrimeFaces, but it does for 3.4.2:

不过,我找到了一个很好的解决方法。可以通过在数据表的窗口小部件上调用未记录的JavaScript方法来触发PrimeFaces表的排序。以下可能不适用于PrimeFaces的未来版本,但它适用于3.4.2:

Just add the following to your component, which triggers the update:

只需将以下内容添加到组件中即可触发更新:

oncomplete="myTableWidget.sort($(PrimeFaces.escapeClientId('#{p:component('sortColumnId')}')), 'ASCENDING')"

The table might look like this:

该表可能如下所示:

<p:dataTable id="myTable"
             widgetVar="myTableWidget"
             value="#{myArticles}"
             var="article"
             sortBy="#{article.price}"
             sortOrder="ASCENDING">
    ...

    <p:column id="price" sortBy="#{article.price}">
        <f:facet name="header">
            <h:outputText value="Price" />
        </f:facet>
        <h:outputText value="#{article.price}" />
    </p:column>

</p:dataTable>

So updating the table could work like this.

所以更新表可以这样工作。

<p:commandButton value="refresh" action="#{tableController.refreshPrices}" update="myTable" oncomplete="myTableWidget.sort($(PrimeFaces.escapeClientId('#{p:component('price')}')), 'ASCENDING')" />

#3


3  

EDIT:

编辑:

Solution posted below (LazyTable) works for the p:dataTable backed with LazyDataModel. Overriden load method is called after every update/refresh on the desired table and it handles sort properly. The problem with simple p:dataTable is that it performs predefined sort only on the first load, or after the click on sort column. This is a normal behaviour of a simple table.

下面发布的解决方案(LazyTable)适用于使用LazyDataModel支持的p:dataTable。在所需的表上每次更新/刷新后调用Overriden load方法,它会正确处理排序。简单的p:dataTable的问题在于它仅在第一次加载时或在单击sort列之后执行预定义排序。这是简单表的正常行为。

So what are your possibilities for simple table :

那么简单表的可能性是什么:

  • Don't sort the table after update, but remove the sort column so end user is not missinformed. Add this to your action listener or action method for your update button :

    更新后不要对表进行排序,但删除排序列以便最终用户不会错过信息。将此添加到您的更新按钮的动作侦听器或操作方法:

    UIComponent table  = FacesContext.getCurrentInstance().getViewRoot().findComponent(":dataTablesForm:dataTableId");
    table.setValueExpression("sortBy", null);
    
  • Update the sort of the table manually by script. This is not the best solution, but primefaces doesn't provide any client side function for "resorting" the table. Basically you know that only one column at a time can be sorted and this column has a .ui-state-active. You can use it in a script and trigger 2 clicks on that column (1. click - other sort order, 2. click - back to current sort order).

    通过脚本手动更新表的种类。这不是最好的解决方案,但是primefaces不提供“求助”表的任何客户端功能。基本上,您知道一次只能对一列进行排序,并且此列具有.ui-state-active。您可以在脚本中使用它并触发该列的2次单击(1.单击 - 其他排序顺序,2。单击 - 返回当前排序顺序)。

     <h:form id="mainForm">  
    <div id="tableDiv">
       <p:dataTable id="dataTable" var="item" value="#{testBean.dummyItems}">
          .
          .
          .
       </p:dataTable> 
       <p:commandButton value="Refresh" oncomplete="refreshSort();" update=":mainForm:dataTable"/>
     </div>
    

    And script function :

    和脚本功能:

    function refreshSort(){
    jQuery('#tableDiv').find('.ui-state-active').trigger('click');
    jQuery('#tableDiv').find('.ui-state-active').trigger('click');
    }
    

I know this is not the best workaround, but it works. You can use it as an inspiration to make something better.

我知道这不是最好的解决方法,但它确实有效。你可以用它作为灵感来创造更好的东西。

LazyTable

IMHO the most proper way is to update directly the component you want. So for example :

恕我直言,最恰当的方法是直接更新你想要的组件。例如:

<h:form id="dataTableForm">
  <p:dataTable id="dataTableToUpdate">
    .
    .
    .
  </p:dataTable>
  <p:commandButton value="Refresh" update=":dataTableForm:dataTableToUpdate" />
</h:form>

It should work fine in this scenario (I suppose it is your purpose) : Open the .xhtml with your p:dataTable, sort some column (keep the facelet opened), update dataTables data from another .xhtml for example, click on refresh button. The dataTable should show your added value in correct (previously chosen) sort order - sorting was performed after update.

它应该在这种情况下工作正常(我想这是你的目的):用你的p:dataTable打开.xhtml,排序一些列(保持facelet打开),从另一个.xhtml更新dataTables数据,例如,点击刷新按钮。 dataTable应以正确(先前选择的)排序顺序显示您的添加值 - 更新后执行排序。

Hope it helped !

希望它有所帮助!

#4


1  

Add this PhaseListener to your application and both sorting and filtering will be kept after updating the DataTable.

将此PhaseListener添加到您的应用程序中,更新DataTable后将保留排序和过滤。

public class DataTableUpdatePhaseListener implements PhaseListener {

    private static final long serialVersionUID = 1L;

    @Override
    public void afterPhase(PhaseEvent event) {
        // Nothing to do here
    }

    @Override
    public void beforePhase(PhaseEvent event) {
        FacesContext facesContext = event.getFacesContext();
        if (!facesContext.isPostback()) {
            return;
        }

        PartialViewContext partialViewContext = facesContext.getPartialViewContext();
        if (partialViewContext != null) {
            Collection<String> renderIds = partialViewContext.getRenderIds();

            for (String renderId : renderIds) {
                UIComponent component = facesContext.getViewRoot().findComponent(renderId);
                if (component instanceof DataTable) {
                    DataTable table = (DataTable) component;
                    if (!table.isLazy()) {
                        updateDataTable(table);
                    }
                }
            }
        }
    }

    @Override
    public PhaseId getPhaseId() {
        return PhaseId.RENDER_RESPONSE;
    }

    private void updateDataTable(DataTable table) {
        FacesContext facesContext = FacesContext.getCurrentInstance();
        if (facesContext == null || table == null) {
            return;
        }

        // Reapply filtering
        if (!table.getFilters().isEmpty()) {
            FilterFeature filterFeature = new FilterFeature();
            filterFeature.decode(facesContext, table);
        } else {
            table.setFilteredValue(null);
        }

        // Reapply sorting
        ValueExpression tableSortByVE = table.getValueExpression("sortBy");
        if (tableSortByVE != null) {
            String tableSortByExpression = tableSortByVE.getExpressionString();

            // Loop on children, that are the columns, to find the one whose order must be applied.
            for (UIComponent child : table.getChildren()) {
                Column column = (Column) child;
                ValueExpression columnSortByVe = column.getValueExpression("sortBy");
                if (columnSortByVe != null) {
                    String columnSortByExpression = columnSortByVe.getExpressionString();

                    if (tableSortByExpression != null && tableSortByExpression.equals(columnSortByExpression)) {
                        // Now sort table content
                        SortFeature sortFeature = new SortFeature();
                        sortFeature.sort(facesContext, table, tableSortByVE,
                                SortOrder.valueOf(table.getSortOrder().toUpperCase(Locale.ENGLISH)),
                                table.getSortFunction());

                        break;
                    }
                }
            }
        }
    }
}

This is for non-lazy data tables. Data tables using a lazy model do not require this, as the lazy model will take care of sorting and filtering. For non-lazy data tables, this should work with both single and multiple column sorting but there is a bug in Primefaces that makes DataTable loose its MultiSortMeta between postbacks when updating the table. This means that the columns selected for sorting before postback are completely lost from FacesContext and component state and cannot be retrieved anymore upon postback. I'll look more into this later and provide an update if I manage to find a workaround or maybe Primefaces will fix it soon.

这适用于非惰性数据表。使用惰性模型的数据表不需要这样,因为惰性模型将负责排序和过滤。对于非惰性数据表,这应该适用于单列和多列排序,但Primefaces中存在一个错误,使得DataTable在更新表时在回发之间松散其MultiSortMeta。这意味着在回发之前选择用于排序的列从FacesContext和组件状态完全丢失,并且在回发时不再检索。如果我设法找到一个解决方法,或者Primefaces很快就会修复它,我会稍后再详细介绍并提供更新。

#5


0  

I implement a Comparator with my toString() method

我用toString()方法实现了一个Comparator

municipios = municipioFacade.findAll();
Collections.sort(municipios, new DefaultComparator());
UIComponent table = FacesContext.getCurrentInstance().getViewRoot().findComponent(":municipios:municipios-tabla");
    table.setValueExpression("sortBy", null);

comparator

比较

public class DefaultComparator implements Comparator {

    @Override
    public int compare(Object o1, Object o2) {
        return o1.toString().compareToIgnoreCase(o2.toString());
    }
}

#6


0  

The solution is to have a conversational bean. In this case the p:dataTable refreshes the table and its entries without affecting the sort order. In the case when you have a p:commandButton on each line the table refresh works correctly too.

解决方案是拥有一个会话bean。在这种情况下,p:dataTable刷新表及其条目而不影响排序顺序。如果每行都有一个p:commandButton,表刷新也能正常工作。

The conversational bean:

会话bean:

@Named
@Stateful
@ConversationScoped
public class ItemBean {

    @Inject
    private Conversation conversation;

    private List<Item> items;

    public List<Item> getItems() {
        return this.items;
    }

    public void retrieve() {
        // if it's an Ajax call then avoid retrieving items
        if (FacesContext.getCurrentInstance().isPostback()) {
            return;
        }
        // start a conversation
        if (this.conversation.isTransient()) {
            this.conversation.begin();
        }
        this.items = retrieveItems();
    }
}

The associated page:

关联页面:

<f:metadata>
    <f:event type="preRenderView" listener="#{itemBean.retrieve}" />
</f:metadata>

<h:form id="form">
    <p:dataTable id="table" var="_item" value="#{testBean.items}">
    ... <!-- you can have the p:commandButton in a column too, to refresh the respective column only
        <p:commandButton value="Refresh" action="#{itemBean.update(_item)}" 
        -->
    </p:dataTable> 
    <p:commandButton value="Refresh" action="#{itemBean.update}" update=":form:table"/>
</h:form>

#7


0  

I had the same problem as you. I was able to fix the issue using a LazyDataModel. Because of the PrimeFaces issue with the row index, I needed to add the extra UtilitiesLazyDataModel(see row index comments):

我遇到了和你一样的问题。我能够使用LazyDataModel解决问题。由于PrimeFaces问题与行索引,我需要添加额外的UtilitiesLazyDataModel(请参阅行索引注释):

public abstract class UtilitiesLazyDataModel <T> extends LazyDataModel<T>{

    public UtilitiesLazyDataModel() {
    }    

    @Override
    public void setRowIndex(int rowIndex) {
        /*
         * The following is in ancestor (LazyDataModel):
         * this.rowIndex = rowIndex == -1 ? rowIndex : (rowIndex % pageSize);
         */
        if (rowIndex == -1 || getPageSize() == 0) {
            super.setRowIndex(-1);
        } else {
            super.setRowIndex(rowIndex % getPageSize());
        }      
    }
}

Then use your LazyDataModel class with this:

然后使用您的LazyDataModel类:

public class MyDataModel extends UtilitiesLazyDataModel<MyObjectClass>{

//override getRowData and getRowKey

}

#8


0  

@Rares Oltean's approach could also be implemented using preRenderView event listener. On your jsf page register listener:

@Rares Oltean的方法也可以使用preRenderView事件监听器来实现。在你的jsf页面注册监听器:

<f:event listener="#{managedBean.preRenderViewListener}" type="preRenderView" />

and implement it in a ManagedBean:

并在ManagedBean中实现它:

public void preRenderViewListener() {
    FacesContext facesContext = FacesContext.getCurrentInstance();
    if (!facesContext.isPostback()) {
        return;
    }

    PartialViewContext partialViewContext = facesContext.getPartialViewContext();
    if (partialViewContext != null) {
        Collection<String> renderIds = partialViewContext.getRenderIds();

        for (String renderId : renderIds) {
            UIComponent component = facesContext.getViewRoot().findComponent(renderId);
            if (component instanceof DataTable) {
                DataTable table = (DataTable) component;
                if (!table.isLazy()) {
                    updateDataTable(table);
                }
            }
        }
    }
}

private void updateDataTable(DataTable table) {
    FacesContext facesContext = FacesContext.getCurrentInstance();
    if (facesContext == null || table == null) {
        return;
    }
    if (!table.getFilters().isEmpty()) {
        FilterFeature filterFeature = new FilterFeature();
        filterFeature.decode(facesContext, table);
    } else {
        table.setFilteredValue(null);
    }
    ValueExpression tableSortByVE = table.getValueExpression("sortBy");
    if (tableSortByVE != null) {
        String tableSortByExpression = tableSortByVE.getExpressionString();
        for (UIComponent child : table.getChildren()) {
            Column column = (Column) child;
            ValueExpression columnSortByVe = column.getValueExpression("sortBy");
            if (columnSortByVe != null) {
                String columnSortByExpression = columnSortByVe.getExpressionString();
                if (tableSortByExpression != null && tableSortByExpression.equals(columnSortByExpression)) {
                    SortFeature sortFeature = new SortFeature();
                    sortFeature.sort(facesContext, table, tableSortByVE, table.getVar(),
                            SortOrder.valueOf(table.getSortOrder().toUpperCase(Locale.ENGLISH)),
                            table.getSortFunction());
                    break;
                }
            }
        }
    }
}