p:dataTable selections are lost after paginating a LazyDataModel

Mr.J4mes picture Mr.J4mes · Aug 31, 2013 · Viewed 11.4k times · Source

My problem is that after I've selected a few items on the 1st page, if I paginate to another page and come back, my initial selections are not shown. I've tried to implement the SelectableDataModel as well as using the rowKey attribute but the problem persists.

This is my test bean:

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

This is my test page:

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

I'd be very grateful if you could show me what I've done wrong here.

UPDATE: After I added more System.out.println("TEST") to the above code, I observed the following things:

  1. On the console, every time I paginate, TEST 1: getRowData is always printed before TEST 2: load. As a consequence, I believe the method #LazyDataModel.getWrappedData() may return data from the old page. At first, I thought this method's goal was to retrieve the selected rows to highlight on the table. However, if this method is called before load, there's no way it can do the job right?
  2. After I selected the 1st 2 items on the 1st page, on the console, I saw TEST 3: chosenItems's size: 2. If I paginate to the 2nd page and then back to the 1st page, the selections are lost as mentioned. However, if I continued to select another item, on the console, I saw TEST 3: chosenItems's size: 3. Obviously, the chosenItems list still kept my old selections but they're not rendered on the table.

Answer

Bruno Andrade picture Bruno Andrade · Sep 20, 2017

In webPage just add a event for when page switch:

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

In the listingBean, just save the selected:

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