【问题标题】:p:datatable loses sort column and order after ajax refreshp:datatable 在 ajax 刷新后丢失排序列和顺序
【发布时间】:2012-04-19 09:57:03
【问题描述】:

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

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

这一切都很好,除了刷新表时它会恢复为不排序任何内容,同时仍然显示它是基于先前值排序的。换句话说,标题仍然突出显示,箭头仍然指向排序方向,但实际上没有执行排序。显然这并不理想。

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

据我所知,表格何时需要排序,它会发布以下选项:

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

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

【问题讨论】:

    标签: ajax sorting jsf-2 primefaces datatable


    【解决方案1】:

    编辑:

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

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

    • 更新后不要对表进行排序,而是删除排序列,以免最终用户被误传。将此添加到更新按钮的操作侦听器或操作方法中:

      UIComponent table  = FacesContext.getCurrentInstance().getViewRoot().findComponent(":dataTablesForm:dataTableId");
      table.setValueExpression("sortBy", null);
      
    • 通过脚本手动更新表格的排序。这不是最好的解决方案,但 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>
      

      以及脚本功能:

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

    我知道这不是最好的解决方法,但它确实有效。您可以以此为灵感,让事情变得更好。

    懒惰表

    恕我直言,最合适的方法是直接更新您想要的组件。例如:

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

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

    希望对您有所帮助!

    【讨论】:

    • 所以排序是为你维护的?或者排序按钮只是突出显示,但实际排序是内存中对象的排序顺序?我尝试直接更新 DT,但这对我的行为没有影响。
    • 排序按钮高亮显示,排序正确。对象的排序方式与内存中的不同 - 取决于实际的排序顺序。我已经为发布的场景尝试过它,它对我来说完全没问题。您对 dataTable bean 使用什么范围?
    • 我已经编辑了答案,让我知道它是否适合你。
    • 好吧,惰性表选项似乎对我不起作用。如果我更新表格,排序会被重置。我将不得不测试另一个选项,但我想它会起作用。删除 sortby 有什么作用?
    • 我使用了您的 jQuery 解决方案,效果很好。我所做的唯一更改是: function refreshSort(selector){ jQuery(selector).find('.ui-state-active.ui-sortable-column').trigger('click'); jQuery(selector).find('.ui-state-active.ui-sortable-column').trigger('click'); } 并在数据表中:
    【解决方案2】:

    我用我的 toString() 方法实现了一个比较器

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

    比较器

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

    【讨论】:

      【解决方案3】:

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

      会话 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();
          }
      }
      

      相关页面:

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

      【讨论】:

        【解决方案4】:

        首先,PrimeFaces Issue Tracker 上有一张未解决的票,它针对这个问题,但最近被标记为“不会修复”。

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

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

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

        表格可能如下所示:

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

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

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

        【讨论】:

        • 在 PF6.1 中排序顺序必须是一个数字:1 / -1 表示“升序”/“降序”。
        【解决方案5】:

        我为@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);
        }
        

        像这样更新与 truemmer 相同的表:

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

        编辑: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);
        }
        

        【讨论】:

        • 不错的解决方案。我很快会升级到 3.5 并试一试 :) 在当前版本中对表进行了很多重构。
        • 我应该注意到我在 3.4.2 中一直在使用它。我不知道这在 3.5.* 中是否很好用,但我怀疑我是否会升级。
        • 在 PrimeFaces 4.0 下仍然有效,但我正在尝试改进它以管理多排序
        • 还没有找到一种方法来执行“datatable.sort(List selectedColumns, List sortOrders)”但是如果有人找到它我会很感激的。
        • @AlvaroCachoperro,我刚刚编辑了多排序支持
        【解决方案6】:

        我和你有同样的问题。我能够使用LazyDataModel 解决此问题。因为PrimeFaces issue with the row index,我需要添加额外的UtilitiesLazyDataModel(见行索引cmets):

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

        然后使用您的LazyDataModel 类:

        public class MyDataModel extends UtilitiesLazyDataModel<MyObjectClass>{
        
        //override getRowData and getRowKey
        
        }
        

        【讨论】:

          【解决方案7】:

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

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

          【讨论】:

            【解决方案8】:

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

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

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

            【讨论】:

              猜你喜欢
              • 2011-11-14
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-02-22
              • 2023-03-27
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多