19

我的问题是,在我选择了第一页上的一些项目后,如果我分页到另一页并返回,我的初始选择不会显示。我试图实现SelectableDataModel以及使用该rowKey属性,但问题仍然存在。

这是我的测试豆:

@ManagedBean
@ViewScoped
public class MrBean {
    private List<Item> chosenItems;
    private LazyDataModel lazyModel;

    @PostConstruct
    public void prepareTest() {
        this.lazyModel = new LazyItemDataModel();
    }

    public void countItems() {
        System.out.println("TEST 3: chosenItems's size: " + chosenItems.size());
    }

    private class LazyItemDataModel extends LazyDataModel<Item> implements SelectableDataModel<Item> {
        @Override
        public Item getRowData(String rowKey) {
            System.out.println("TEST 1: getRowData");
            Iterator<Item> iter = ((List<Item>) this.getWrappedData()).iterator();
            while (iter.hasNext()) {
                Item item = iter.next();
                if (item.getId().equals(rowKey)) {
                    return item;
                }
            }

            return null;
        }

        @Override
        public Object getRowKey(Item item) {
            return item.getId();
        }

        @Override
        public List<Item> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map filters) {
            System.out.println("TEST 2: load");
            // Code to retrieve items from database
        }
    }

    // Getters and Setters
}

这是我的测试页面:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui">
    <h:head>
        <title>Test page</title>
    </h:head>
    <h:body>
        <h:form>
            <p:dataTable id="itemTable" var="item" value="#{mrBean.items}" rows="5" 
                         paginator="true" selection="#{mrBean.chosenItems}" lazy="true" >

                <p:ajax event="rowSelectCheckbox" listener="mrBean.countItems" />                    

                <p:column selectionMode="multiple" />

                <p:column headerText="ID">
                    <h:outputText value="#{item.id}" /> 
                </p:column>

                <p:column headerText="Name">
                    <h:outputText value="#{item.name}" /> 
                </p:column>

            </p:dataTable>
        </h:form>
    </h:body>
</html>

如果你能告诉我我在这里做错了什么,我将不胜感激。

更新:在上面的代码中添加更多内容后System.out.println("TEST"),我观察到以下内容:

  1. 在控制台上,每次我分页时,TEST 1: getRowData总是在TEST 2: load. 因此,我相信该方法#LazyDataModel.getWrappedData()可能会从旧页面返回数据。起初,我认为此方法的目标是检索选定的行以在表格中突出显示。但是,如果之前调用了这个方法load,它就无法完成这项工作吗?
  2. 在我选择第一页上的第 2 项后,在控制台上,我看到了TEST 3: chosenItems's size: 2. 如果我分页到第 2 页然后返回到第 1 页,则选择会丢失,如前所述。但是,如果我继续选择另一个项目,在控制台上,我看到TEST 3: chosenItems's size: 3. 显然,该chosenItems列表仍然保留了我的旧选择,但它们没有呈现在桌面上。
4

5 回答 5

4

在 webPage 中,只需为页面切换添加一个事件:

<p:ajax event="page" listener="#{listingBean.updateSelected()}" />

在listingBean中,只保存选中的:

private List<Entity> selectedInstances;
private List<Entity> selectedInstancesSaved;

public List<Entity> getSelectedInstances()
{
    return selectedInstancesSaved;
}

public void setSelectedInstances(List<Entity> selectedInstances)
{
    this.selectedInstances = selectedInstances;
}

public void updateSelected()
{
    if (selectedInstances != null && !selectedInstances.isEmpty()) {
        for (Entity inst : lazyModel.getDatasource()) {
            if (selectedInstances.contains(inst)) {
                selectedInstancesSaved.add( inst);
            } else {
                selectedInstancesSaved.remove( inst);
            }
        }
    }
}
于 2017-09-20T09:08:53.950 回答
3

虽然 Bruno 的解决方案适用于跨页保留选择,但它不考虑在单个页面上保留选择(即,当从不更改页面时)。

这个问题可以通过使用rowSelectCheckboxrowUnselectCheckbox ajax 事件更简单地解决,此外还有一个单独的“已保存”行列表。

JSF:

 <p:dataTable selection="#{myBean.selectedRows}" ... >
   <p:ajax event="rowSelectCheckbox" process="@this" listener="#{myBean.onSelectRow}" />
   <p:ajax event="rowUnselectCheckbox" process="@this" listener="#{myBean.onUnselectRow}" />
   <p:column selectionMode="multiple" ... />
    ...
 </p:dataTable>

支持豆:

    private List<MyRowClass> selectedRows;
    private List<MyRowClass> selectedRowsSaved;

    ...

    public void onSelectRow(SelectEvent event){
        MyRowClass row = (MyRowClass) event.getObject();
        selectedRowsSaved.add(row);
    }

    public void onUnselectRow(UnselectEvent event){
        MyRowClass row = (MyRowClass) event.getObject();
        selectedRowsSaved.remove(row);
    }

    public List<MyRowClass> getSelectedRows(){
        return selectedRowsSaved;
    }

    public void setSelectedRows(List<MyRowClass> selectedRows){
        this.selectedRows = selectedRows;
    }

这样,保存的行列表始终保持最新,而不需要“页面”ajax 事件。

于 2018-04-19T14:45:22.167 回答
3

只需像这样实现绑定到 DataTable() 的选择属性的属性,selection="#{pageBackingForm.selectedEntityList}"它将起作用:

    private Map<Integer, List<Entity>> selectedEntityListMap = new Hashtable<>();

    public List<Entity> getSelectedEntityList() {
        return selectedEntityListMap.get(getCurrentEntitySelectionPage());
    }

    public void setSelectedEntityList(List<Entity> selectedEntityList) {
        if (selectedEntityList == null) {
            selectedEntityListMap.remove(getCurrentEntitySelectionPage());
            return;
        }

        selectedEntityListMap.put(getCurrentEntitySelectionPage(), selectedEntityList);
    }

    public Integer getCurrentEntitySelectionPage() {
        DataTable dataTable = (DataTable) FacesContext.getCurrentInstance().getViewRoot().findComponent("formId:dataTableId");
        return dataTable.getPage();
    }
于 2015-08-31T14:20:39.840 回答
2

这是因为在SelectionFeature解码时会创建一个新列表。

如果table.getRowData(rowKeys[i])(与您的LazyDataModel实现相关)返回 null 您在上一页中的旧选择已经消失。可以尝试通过更改您的 LazyDataModel 实现来解决它我没有尝试这些但看看这个这个

有同样的问题,我认为如果你有很多不同的表实现 LazyDataModel,这个解决方案会更容易。

这就是我所做的:首先检查它是否是惰性的,然后将当前选定的行添加到 selectionList。

对于素面 4.0

1)覆盖DataTableRenderer

在 faces-config.xml 中

<render-kit>
   <renderer>
       <component-family>org.primefaces.component</component-family>
       <renderer-type>org.primefaces.component.DataTableRenderer</renderer-type>
       <renderer-class>com.package.LazyDataTableRenderer</renderer-class>
   </renderer>
</render-kit>

public class LazyDataTableRenderer extends DataTableRenderer {
static Map<DataTableFeatureKey,DataTableFeature> FEATURES;

    static {
        FEATURES = new HashMap<DataTableFeatureKey,DataTableFeature>();
        FEATURES.put(DataTableFeatureKey.DRAGGABLE_COLUMNS, new DraggableColumnsFeature());
        FEATURES.put(DataTableFeatureKey.FILTER, new FilterFeature());
        FEATURES.put(DataTableFeatureKey.PAGE, new PageFeature());
        FEATURES.put(DataTableFeatureKey.SORT, new SortFeature());
        FEATURES.put(DataTableFeatureKey.RESIZABLE_COLUMNS, new ResizableColumnsFeature());
        FEATURES.put(DataTableFeatureKey.SELECT, new LazySelectionFeature());
        FEATURES.put(DataTableFeatureKey.ROW_EDIT, new RowEditFeature());
        FEATURES.put(DataTableFeatureKey.CELL_EDIT, new CellEditFeature());
        FEATURES.put(DataTableFeatureKey.ROW_EXPAND, new RowExpandFeature());
        FEATURES.put(DataTableFeatureKey.SCROLL, new ScrollFeature());
    }

    @Override
    public void decode(FacesContext context, UIComponent component) {
        DataTable table = (DataTable) component;

        for(Iterator<DataTableFeature> it = FEATURES.values().iterator(); it.hasNext();) {
            DataTableFeature feature = it.next();

            if(feature.shouldDecode(context, table)) {
                feature.decode(context, table);
            }
        }

        decodeBehaviors(context, component);        
    }
}

2)覆盖SelectionFeature的解码

更新:已编辑以允许取消选择

public class LazySelectionFeature extends org.primefaces.component.datatable.feature.SelectionFeature{

    @Override
    public void decode(FacesContext context, DataTable table) {
        String clientId = table.getClientId(context);
        Map<String,String> params = context.getExternalContext().getRequestParameterMap();

        String selection = params.get(clientId + "_selection");

        if(table.isSingleSelectionMode())
            decodeSingleSelection(table, selection);
        else
            decodeMultipleSelection(context, table, selection);
    }

    void decodeSingleSelection(DataTable table, String selection) {
            if(ComponentUtils.isValueBlank(selection))
                table.setSelection(null);
            else
                table.setSelection(table.getRowData(selection));
        }

    void decodeMultipleSelection(FacesContext context, DataTable table, String selection) {
        Class<?> clazz = table.getValueExpression("selection").getType(context.getELContext());
        boolean isArray = clazz.isArray();

        if(!isArray && !List.class.isAssignableFrom(clazz)) {
            throw new FacesException("Multiple selection reference must be an Array or a List for datatable " + table.getClientId());
        }

        if(ComponentUtils.isValueBlank(selection)) {
            if(isArray) {
                table.setSelection(Array.newInstance(clazz.getComponentType(), 0));
            }
            else {
                table.setSelection(new ArrayList<Object>());
            }
        }
        else {
            String[] rowKeys = selection.split(",");
            List<Object> selectionList = new ArrayList<Object>();

            boolean lazy=table.isLazy();
            if (lazy) {

            List<String> currentRowKeys = new ArrayList<String>(Arrays.asList(rowKeys));
            if (table.getSelection() != null) {
                List<Object> alreadySelected = (List<Object>) table.getSelection();

                for (Object object : alreadySelected) {//For deselecting
                    Object rowKeyFromModel = table.getRowKeyFromModel(object);
                    if (currentRowKeys.contains(rowKeyFromModel)) {
                        selectionList.add(object);
                        currentRowKeys.remove(rowKeyFromModel);
                    } 
                }                    
            }
            for (String key : currentRowKeys) {//For selecting  
                Object rowData = table.getRowData(key);
                if (rowData != null && !selectionList.contains(rowData)) {
                       selectionList.add(rowData);
                }
            }

        }else{

                for(int i = 0; i < rowKeys.length; i++) {
                    Object rowData = table.getRowData(rowKeys[i]);

                    if(rowData != null)
                        selectionList.add(rowData);
                }

            }

            if(isArray) {
                Object selectionArray = Array.newInstance(clazz.getComponentType(), selectionList.size());
                table.setSelection(selectionList.toArray((Object[]) selectionArray));
            }
            else {
                table.setSelection(selectionList);
            }
        }
    }
}

可能不是最好的解决方案,但应该可以,如果有更好的方法,请告诉我。希望这可以帮助某人。

于 2014-06-23T07:40:05.730 回答
-1

我的数据表也有同样的问题。虽然我的情况有点不同,因为我使用的是selectBooleanCheckbox。我找到了一个适合我的简单解决方案。当您说“旧选择未在表格中呈现”时,我感到很震惊。

  • a4j:support 事件绑定复选框

代码:

<h:selectBooleanCheckbox value="#{batch.toPortfolio}">
    <a4j:support event="onchange" />
</h:selectBooleanCheckbox>
于 2013-11-07T04:16:33.120 回答