PrimeFaces Datatable: How to receive selected rows values?

9.8k Views Asked by At

I use Prime Faces 6.2 to make a data table with checked column:

<p:dataTable id="#{prefix}List"
                 value="#{tickets}"
                 lazy="true"
                 paginator="true"
                 paginatorTemplate="{FirstPageLink} {PreviousPageLink} {CurrentPageReport} {NextPageLink} {LastPageLink}"
                 currentPageReportTemplate="{startRecord}-{endRecord} из {totalRecords}"
                 rows="20"
                 rowKey="#{ticket.id}"
                 var="ticket"
                 emptyMessage="Записи в данной категории отсутствуют">

    <p:ajax event="toggleSelect" listener="#{ticketForm.onAllSelect}" process="@this" partialSubmit="true" />
    <p:ajax event="rowSelectCheckbox" listener="#{ticketForm.onSelect}" update=":protocolForm" />
    <p:ajax event="rowUnselectCheckbox" listener="#{ticketForm.onUnselect}" update=":protocolForm" />
    <p:ajax event="rowSelect" listener="#{ticketForm.onSelect}" update=":protocolForm" />
    <p:ajax event="rowUnselect" listener="#{ticketForm.onUnselect}" update=":protocolForm" />
     
    <p:column selectionMode="multiple" style="width:40px; text-align:center" />
    

More specifically, when a header checkbox is selected I want to receive all selected rows data on server-side and use IDs of each row. Also, there's logic that helps to resolve a goal of hidding/rendering a page button when at least one checkbox is selected.

For this purpose I need manualy intercept a row/checkbox selection event and record IDs from it when a button pushed, so I can't use selection attribute of dataTable using such logic.

On server-side I have several event-listeners:

private Set<AbstractMTSBUExportTicket> abstractMTSBUExportTickets = new HashSet<>();

public Set<AbstractMTSBUExportTicket> getAbstractMTSBUExportTickets() {
    return abstractMTSBUExportTickets;
}

public void onSelect(SelectEvent event) {
    abstractMTSBUExportTickets.add((AbstractMTSBUExportTicket) event.getObject());
}

public void onUnselect(UnselectEvent event) {
    abstractMTSBUExportTickets.remove(event.getObject());
}

public void onAllSelect(ToggleSelectEvent event) {
    // do smth
}

Unfortunately, ToggleSelectEvent has only information about data-table itself within.

None information about rows I couldn't find. Also I tried to add process="@this" partialSubmit="true" attributes but seems like they do different actions.

So, could you give me an example of how it's possible to retrieve such data from an event object? Should I use another way to solve it? Thanks for your answers in advance.

2

There are 2 best solutions below

1
On

I think you are missing some things on the datatable.

Showcase: https://www.primefaces.org/showcase/ui/data/datatable/selection.xhtml

You need to add selectionMode=multiple and a selection=collection to gather the selected rows like...

<p:dataTable id="multipleDT" 
   var="car" 
   value="#{dtSelectionView.cars4}" 
   selectionMode="multiple" 
   selection="#{dtSelectionView.selectedCars}" 
   rowKey="#{car.id}">

Whenever you select a row or row(s) the #{dtSelectionView.selectedCars} collection will be filled with the selected rows automatically.

0
On

You're use lazy dataTable and this doesn't stay the selection on page change.

  1. You need set the selection param with a type of your collection

  2. Implement getRowKey inside your LazyDataModel and remove rowKey="#{ticket.id}"

  3. Selection with checkbox doesn't need selectionMode="multiple".

    • This param is only for multiple selection when click on the table row with CTRL is pressed
  4. My sugestion is control your selected data manualy

XHTML file:

<p:dataTable id="#{prefix}List"
                 value="#{tickets}"
                 lazy="true"
                 paginator="true"
                 var="ticket"
                 selection="#{ticketForm.selectedTickets}"
                 emptyMessage="Записи в данной категории отсутствуют">

    <p:ajax event="toggleSelect" 
            listener="#{ticketForm.onAllSelect}" partialSubmit="true"/>
    <p:ajax event="rowSelectCheckbox" 
            listener="#{ticketForm.onSelect}" update=":protocolForm"/>
    <p:ajax event="rowUnselectCheckbox" 
            listener="#{ticketForm.onUnselect}" update=":protocolForm"/>
    <p:ajax event="rowSelect" 
            listener="#{ticketForm.onSelect}" update=":protocolForm"/>
    <p:ajax event="rowUnselect" 
            listener="#{ticketForm.onUnselect}" update=":protocolForm"/>

    <p:column selectionMode="multiple" style="width:40px; text-align:center" />
    <!--other p:columns-->
</p:dataTable>

Managed bean:

public void onRowSelect(SelectEvent event) {
    if (event != null && event.getObject() != null && 
        event.getObject() instanceof Ticket) {
        if (selectedTickets== null) {
            selectedTickets= new ArrayList<Ticket>();
        }
        if (!selectedTickets.contains((Ticket) event.getObject())) {
            selectedTickets.add((Ticket) event.getObject());
        }
    }
}

public void onRowUnselect(UnselectEvent event) {
    if (event != null && event.getObject() != null && 
        event.getObject() instanceof Ticket && 
        selectedTickets != null && selectedTickets.contains((Ticket) event.getObject())) {
        selectedTickets.remove((Ticket) event.getObject());
    }
}

public void onAllRowsSelect(ToggleSelectEvent event) {
    //This is the trick, you don't need receive a collection
    if (event.isSelected()) {
        selectedTickets = ticketService.getAllTickets();
    } else {
        selectedTickets = new ArrayList<Ticket>();
    }
}

LazyDataModel implemention of getRowKey method:

public class LazyPragaModel extends LazyDataModel<Ticket> implements Serializable {

    private TicketService ticketService;
    private static final long serialVersionUID = 1L;

    public LazyPragaModel(TicketService ticketService) {
        this.ticketService= ticketService;
    }

    @Override
    public Object getRowKey(Ticket ticket) {
        return ticket!= null ? ticket.getId() : null;
    }

    @Override
    public Praga getRowData(String rowKey) {
        List<Ticket> tickets = (List<Ticket>) getWrappedData();
        for (Ticket ticket: tickets) {
            if (ticket.getId().toString().endsWith(rowKey)) {
                return ticket;
            }
        }
        return null;
    }

    @Override
    public List<Ticket> load(int first, int pageSize, String sortField, 
                             SortOrder sortOrder, Map<String, Object> filters) {
        setRowCount(ticketService.countLazyRecords(filters).intValue());
        List<Ticket> tickets = ticketService.listLazyRecords(first, pageSize, 
                                                     sortField, sortOrder.name(), filters);
        return tickets;
    }
}

Sugestion for your service. Generic methods for lazy dataTables:

public List<T> listLazyRecords(int first, int pageSize, String sortField, 
                               String sortOrder, Map<String, Object> filters) {
    sortOrder = sortOrder != null ? sortOrder.contains("ASC") ? "ASC" : 
                                    sortOrder.contains("DESC") ? "DESC" : null : null; 
    String query = " FROM " + getType().getSimpleName() + " t " + 
                  (filters.size() > 0 ? buildLazyFilters(filters) : "") + 
                  (sortOrder != null ? " ORDER BY " + sortField + " " + sortOrder : "");
    return getManager().createQuery(query, getType())
                       .setFirstResult(first).setMaxResults(pageSize).getResultList();
}

public Long countLazyRecords(Map<String, Object> filters) {
    String query = "SELECT COUNT(x) FROM " + getType().getSimpleName() + 
                   " x " + (filters.size() > 0 ? buildLazyFilters(filters) : "");
    return getManager().createQuery(query).getSingleResult();
}

private String buildLazyFilters(Map<String, Object> filters) {
    StringBuilder filterBuild = new StringBuilder("WHERE ");
    for (Map.Entry<String, Object> filter : filters.entrySet()) {
       if (filter.getValue().toString().chars().allMatch(Character::isDigit)) {
            filterBuild.append("( " + filter.getKey() + " = " + 
                                      filter.getValue() + " OR ");
       }
       filterBuild.append(filter.getKey() + " LIKE '%" + 
                          filter.getValue().toString() + "%'");
       filterBuild.append(filter.getValue().toString().chars()
                                  .allMatch(Character::isDigit) ? ") ": "");
       filterBuild.append(!filters.values().toArray()[filters.size() - 1]
                                  .equals(filter.getValue()) ? " AND " : "");
    }
    return filterBuild.toString();
}