primefaces datatable select all with paginator - jsf

I have a p:datatable with multiple selection mode and a paginator.
<p:datatable value=#{rows} selection=#{selectedRows} pagintor="true" rows="20" rowsPerPageTemplate="10,20,50,100" paginatorPosition="bottom">
<p:column selectionMode="multiple"/>
<p:ajax event="toggleSelect"/>
... columns ...
</p:datatable>
When i click the header checkbox all rows are selected. When i click on the header checkbox to select all rows and then want to export the selected rows (by using the values of 'selection'), it only returns 20 objects. I expect that when i use the header checkbox it selects all rows of the datatable, and not only the one of the page. I have a datatable with more then 200 pages, so you can imagine it is a very tedious job to export all of the when using a paginator ;).
I suppose it is a bug I should log, or am I missing something? I looked through the documentation but it says nothing about using multiple selection mode together with pagination... Thanks for your feedback!

I had the exact same requirement - select all rows across pages.
What I did - provided an <p:inputSwitch /> to toggle all rows across all the pages.
Copied and is as below -
<p:dataTable id="id" var="var" widgetVar="wvar" rowsPerPageTemplate="7,15" reflow="true"
value="#{backingBean.dataModel}" paginator="true" rows="7" rowIndexVar="index" rowKey="#{var.id}"
paginatorTemplate="{RowsPerPageDropdown} {FirstPageLink} {PreviousPageLink} {CurrentPageReport} {NextPageLink} {LastPageLink}"
selection="#{backingBean.selectedItems}">
<c:facet name="header"...
<p:inputSwitch value="#{backingBean.allRecords}" onLabel="All" offLabel="None" >
<p:ajax onstart="if (PF('wvar').getSelectedRowsCount() == 0) PF('wvar').selectAllRows(); else PF('wvar').unselectAllRows();" />
</p:inputSwitch>
private boolean allRecords;//Getter++Setter++
And, defined toggleSelect event as below -
<p:ajax event="toggleSelect" onstart="if (PF('wvar').getSelectedRowsCount() == 7 || PF('wvar').getSelectedRowsCount() == 15) PF('wvar').selectAllRows(); else PF('wvar').unselectAllRows();" />
where 7 and 15 comes from the datatable rowsPerPageTemplate="7,15".
Works for me.(PrimeFaces 5.3)

FOR JSF 2 ,for selected all rows on datatable in selectionMode multiple with paginator=true:
In Page
<p:dataTable widgetVar="tableArea" yourtags...>
<p:ajax event="toggleSelect" oncomplete="teste()" /> /// toggleSelect is dispared on click to checkbox header
<p:column id="columnId" selectionMode="multiple"/>
In js:
function teste(){
var checked = $(document).find(":checkbox")["0"].checked; ///Find checkbox header and verify if checkbox is checked
if(checked == true){
PF('tableArea').selectAllRows(); // if true, selectAllRows from datatable
} else {
PF('tableArea').unselectAllRows(); //
}
}

Sample Code:-
**JAVA (your backing bean class)
==============================**
//Stores the checked items from data table.
private List<String> selectedIds = new ArrayList<>();
private List<String> getSomeList() {
// return list of strings to data table
return someList;
}
public void selectAllCheckboxes(ToggleSelectEvent event) {
if (selectedIds != null) {
selectedIds.clear();
if (event.isSelected()) {
selectedIds.addAll(someList); //Add all the elements from getSomeList()
}
}
}
**XHTML
=====**
<p:dataTable id="data-table-id" value="#{backingBean.someList}"
selection="#{backingBean.selectedIds}" rowKey="#{id}" var="id"
paginator="true" rows="10" paginatorPosition="bottom"
paginatorAlwaysVisible="false" rowSelectMode="checkbox"
rowsPerPageTemplate="10,20,30,50">
<p:column selectionMode="multiple" />
<p:ajax event="toggleSelect"
update="#this"listener="#backingBean.selectAllCheckboxes}"/>
</p:dataTable>

I expect that when i use the header checkbox it selects all rows of the datatable, and not only the one of the page.
This is a wrong expectation.
I suppose it is a bug I should log,
Nope.
or am I missing something?
No, it is by design. So other than having the wrong expectation you did not miss anything. The fact that you are the first one in SO to ask this and the second one if you count the number of requests in the PrimeFaces forum to is an indication of this.
Not even GMail does this btw. They give you the option to select all message on the page you are on, and when you do this they give you an additional option to select all messages across all pages

Related

PrimeFaces DataTable rowSelect cannot check checkbox

I was trying to "link" <p:ajax event="rowSelect"> and <p:ajax event="rowUnselect"> with <h:selectBooleanCheckbox> so when datatable row is selected, the checkbox would check together and when checkbox is checked, datatable row is selected too.
Problem for now: I can save the state of the checkbox when going to another pagination page but I need to check checkbox then select row then only it will save the state. If I tried to check only checkbox and go to other pagination page, it won't save the state when I come back to the pagination page but if I select row and go to other pagination page, it would save the state. If I select row and check checkbox and go to another pagination page, it won't save the state too. Any idea how to check checkbox and select row when either 1 is clicked and can save state if go to another pagination page?
<p:dataTable value="#{machine.sub}" var="subDir"
id="fileDirectories" rowClasses="row1-whitebg,row2-bluebg"
selectionMode="multiple" selectionPageOnly="false"
rowKey="#{subDir.directory.path}" headerClass="title-column"
rowIndexVar="idx" rows="10" rowsPerPageTemplate="10,20,50" paginator="true"
paginatorAlwaysVisible="false"
paginatorTemplate="#{WebConstant.PAGINATOR_TEMPLATE}">
<p:ajax event="rowSelect" listener="#{machine.selectedRow}"/>
<p:ajax event="rowUnselect" listener="#{machine.unSelectedRow}"/>
<p:column>
<f:facet name="header">
<h:outputText value="#{msg['machine_download']}"/>
</f:facet>
<h:panelGroup id="panelGroupChkSelect">
<h:selectBooleanCheckbox id="chkSelect" value="#{subDir.selected}"
rendered="#{subDir.directory.file}">
<c:if test="#{machineLogExplorerPage.checkButton()}">
<f:attribute name="checked" value="checked"/>
</c:if>
</h:selectBooleanCheckbox>
</h:panelGroup>
</p:column>
</p:dataTable>
Java code
private boolean checked;
public void selectedRow() {
checked = true;
}
public void unSelectedRow() {
checked = false;
}
public boolean checkButton() {
return checked;
}
You don't need to implement the checkbox yourself. Just add a selection column like:
<p:column selectionMode="multiple"/>
To keep track of what is selected use:
<p:dataTable selectionMode="multiple"
selection="#{bean.listOfSelectedItems}"
...>
See:
https://www.primefaces.org/showcase/ui/data/datatable/selection.xhtml
https://primefaces.github.io/primefaces/11_0_0/#/components/datatable?id=multiple-selection-with-checkboxes

How would i validate series selection in primefaces datatable

I have scenario where i need to validate series selection of rows in datatable . I need to enable button when the rows are selected in series and disabled when the are not selected in series.
Disable Button
Enable Button
XTML
<p:dataTable id="listStackFormTable" var="stackLine"
scrollRows="80" scrollable="true" rowIndexVar="rowIndex"
value="#{stackEditBean.listStackLineForm}"
selection="#{stackEditBean.stackLinesSelected}"
sortBy="#{stackLine.serialNo}" editingRow="cell" editable="true"
rowKey="#{stackLine}"
styleClass="table-f no-h-scroll stack-Listing">
<p:ajax event="toggleSelect"
listener="#{stackEditBean.rowSelect}"
update="listStackFormTable" process="#this"></p:ajax>
<p:ajax event="rowSelect" listener="#{stackEditBean.rowSelect}"
update="listStackFormTable" process="#this" />
<p:ajax event="rowSelectCheckbox"
listener="#{stackEditBean.rowSelect}"
update="listStackFormTable" process="#this"></p:ajax>
<p:ajax event="rowUnselectCheckbox"
listener="#{stackEditBean.rowUnSelect}"
update="listStackFormTable" process="#this"></p:ajax>
</p:dataTable>
Java
private List<StackLineForm> stackLinesSelected;
private List<StackLineForm> listStackLineForm;
public void rowSelect(SelectEvent event) {
StackLineForm selectedRow = (StackLineForm) event.getObject();
List<StackLineForm> list = new ArrayList<StackLineForm>();
for (StackLineForm form : this.stackLinesSelected) {
list.add(form);
form.setSelectedStackLineFlag(true);
}
this.getDimHarvestBackingBean().setListSelectedDim(list);
this.setStackLinesSelected(list);
}
public void rowUnSelect(UnselectEvent event) {
StackLineForm selectedRow = (StackLineForm) event.getObject();
List<StackLineForm> list = this.stackLinesSelected;
for (StackLineForm form : this.stackLinesSelected) {
if (selectedRow.getSaKey().equals(form.getSaKey())) {
selectedRow.setSelectedStackLineFlag(false);
list.remove(selectedRow);
}
}
this.getDimHarvestBackingBean().setListSelectedDim(list);
this.setStackLinesSelected(list);
}
Just put an ajax event handler for rowUnselectCheckbox and rowSelectCheckbox, and enable/disable the button in the scenario you want.
<p:ajax event="rowSelectCheckbox" listener="#{bean.eventListener}" />
<p:ajax event="rowUnselectCheckbox" listener="#{bean.eventListener}" />
Give your dataTable's selection attribute a new list, the same type of your current list.
put 4 ajax in your dataTable with listener and update, in listeners check two lists to see if your condition is met, put it in a boolean and use it in the disabled attribute of your buttons. don't forget to update the buttons with client id if they're in another form.
<p:dataTable id="datalist"
value="#{bean.items}" var="item"
selection="#{bean.selectedItems}
editable="true"
editMode="cell"
rowKey="#{item.id}">
<p:ajax event="rowSelect" update="form:disabledBtn form:enabledBtn" listener="#{bean.setBtnDisabled()}"/>
<p:ajax event="rowUnselect" update="form:disabledBtn form:enabledBtn" listener="#{bean.setBtnDisabled()}"/>
<p:ajax event="rowSelectCheckbox" update="form:disabledBtn form:enabledBtn" listener="#{bean.setBtnDisabled()}"/>
<p:ajax event="rowUnselectCheckbox" update="form:disabledBtn form:enabledBtn" listener="#{bean.setBtnDisabled()}"/>
<p:column selectionMode="multiple"/>
you can also do the boolean check, client side with javascript, I don't remember much but the general idea is like, take the dataTable dom in your ajax oncomplete, cycle through the children and check for elements with selected class and check your boolean in there, disable your buttons whether with css and pointer-event or with a remoteCommand to switch your boolean. (its the general idea, there're a little more details).

Primefaces Lazyloading renders rows as html on pagination

I am building a single page application with Primefaces 6.1, JSF 2.2 and running it on Wildfly, which has an option to search for users. Initial view renders only search panel with various filters and on click of search, user table will be queried and set of data is returned and then a block is rendered in the view which contains datatable. Below is the lazyloading code for search users.
public void getUsers(ActionEvent event) {
//few lines to get filterData
this.lazyUsers = new LazyDataModel<UserModel>() {
private static final long serialVersionUID = 1L;
#Override
public List<UserModel> load(int first, int pageSize, String sortField, SortOrder sortOrder,
Map<String, Object> filters) {
filterObject.setStartCount(new Long(first));
filterObject.setCountPerPage(new Long(pageSize));
List<UserModel> list = userService.getUsers(filterObject);
if (list != null)
this.setRowCount(list.get(0).getTotalNumberOfRecords().intValue());
RequestContext context = RequestContext.getCurrentInstance();
if(context!=null)
context.addCallbackParam("totalRecords", this.getRowCount());
return list;
}
};
}
and here's my table view.
<p:outputPanel id="users" autoUpdate="true">
<p:outputPanel rendered="#{users.displayusers}">
<h5 class="title">
<h:outputText value="#{msg.header_title}"></h:outputText>
</h5>
<p:dataTable id="userTable" lazy="true" paginator="true"
rows="2"
emptyMessage="#{msg.emtpymsg}"
paginatorTemplate="{CurrentPageReport} {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
rowsPerPageTemplate="2,4,5"
rendered="#{null != users.lazyUsers && not empty users.lazyUsers }"
value="#{users.lazyUsers}" var="user"
rowIndexVar="userRow">
<p:column headerText="#{msg.Name}">
<h:outputLabel class="tdRow" value="#{user.name}" />
</p:column>
<p:column headerText="#{msg.Phone}">
<h:outputLabel class="tdRow" value="#{user.phone}" />
</p:commandLink>
</p:column>
<p:column headerText="#{msg.Address}">
<h:outputLabel class="tdRow" value="#{user.address}" />
</p:column>
<p:column headerText="#{msg.Email}">
<h:outputLabel class="tdRow" value="#{user.email}" />
</p:column>
</p:dataTable>
</p:outputPanel>
</p:outputPanel>
Everything renders when search happens but then when I paginate it searches for the next page data, and gets the next page data, but it renders it as plain html removing the datatable. On inspection of html, I can only see tr,td as response from server in network tab, and only contents within it are rendered directly into div. The datatable is not preserverd. Could anyone please let me know what's happening here.
I also tried to capture page event within datatable and added update value as
<p:ajax event="page" update="userContainer:userTable"></p:ajax>
but again it did not help. Hope to find some help.
Update
I noticed, this happens for any ajax operation through datatables, like, filtering, sorting, rowsperpage changed etc., Is there any way to identify on exactly what's wrong with this?
You change the value of rendered attribute. Try to encapsulate the dataTable ino another component, and re-render this outer component.
Also could be helpful if you place a dummy table in the template, that is always rendered, EG:
<p:dataTable disabled="true" />
This will make sure all the dataTable needed libraries are loaded at first load of your page...
The solution for this problem was very simple infact.
I had to keep <p:ajax event="page" update="userContainer:userTable"></p:ajax> and rather than update, I had to use process and also with ignoreAutoUpdate="true"
<p:ajax event="page" ignoreAutoUpdate="true" process="userContainer:userTable"></p:ajax>
That fixed the issue. Hope this will be useful to someone in future.

How to filter a p:dataTable with selectionMode with p:selectCheckboxMenu?

I'm trying to filter a p:dataTable with Primefaces component p:selectCheckboxMenu in one column header. This however, does not work as intended. The other filters on the datatable work just fine, such as input fields.
The column in question is the Room type, that has the p:selectCheckboxMenu.
The filtering works once, after that checking or unchecking boxes on the selectCheckbox menu doesn't add or remove any filtering on the table.
Here's an interesting bit on the problem:
If I remove the selectionMode="single" attribute from the datatable, then the sorting works even after the first checkBox toggle. As in, I can toggle and untoggle a box and the p:dataTable gets filtered accordingly. But, I need the selection mode here, since I'm supposed to be able to select a row and navigate to another view by clicking it. That doesn't work when there's no selectionMode attribute on the datatable.
Here's my datatable:
<div class="background">
<div class="freeRoomsContent">
<br/>
<p:outputLabel value="free rooms" styleClass="headerfont"/>
<br/>
<h:form id="freeRoomsForm">
<p:dataTable id="freeRoomsTable" var="room" paginatorPosition="bottom" paginatorAlwaysVisible="false"
value="#{freeRoomsController.freeRoomsList}" selectionMode="single" selection="#{freeRoomsController.room}"
rowKey="#{room.roomId}" widgetVar="freeRoomsTable"
paginator="true" rows="20" pageLinks="5" scrollable="false"
paginatorTemplate="{FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
rowsPerPageTemplate="20,50,100" skipChildren="true" emptyMessage="No free rooms available.">
<p:ajax event="rowSelect" listener="#{freeRoomsController.onRowSelect}" />
<p:column headerText="Room Id" sortBy="#{room.roomId}" filterMatchMode="contains" filterBy="#{room.roomId}">
<h:outputText value="#{room.roomId}"/>
</p:column>
<p:column headerText="Room number" sortBy="#{room.roomNumber}" filterMatchMode="contains" filterBy="#{room.roomNumber}">
<h:outputText value="#{room.roomNumber}" />
</p:column>
<!-- other similar columns -->
<p:column headerText="Room type" filterMatchMode="exact" filterBy="#{room.roomType}">
<f:facet name="filter">
<p:selectCheckboxMenu onchange="PF('freeRoomsTable').filter()"
label="Room type">
<f:selectItems value="#{staticData.roomTypes}" var="rt" itemLabel="#{msg[rt.name]}" itemValue="#{rt.name}"
/>
<p:ajax event="change" process="#this" update="freeRoomsForm" />
<p:ajax event="toggleSelect" process="#this" update="freeRoomsForm" />
</p:selectCheckboxMenu>
</f:facet>
<h:outputText value="#{msg[room.roomtype.name]}">
<f:convertDateTime pattern="dd.MM.yyyy" />
</h:outputText>
</p:column>
<!-- normal input field columns that work -->
</p:dataTable>
</h:form>
</div>
</div>
This worked for me:
Using value and filteredValue in p:dataTable
Use only onchange=PF('freeRoomsTable').filter(), without child p:ajax elements in p:selectCheckboxMenu
Set value attribute for p:selectCheckboxMenu, e.g. value="#{someBean.selectedItems}"
In your bean, use private String[] selectedItems, with getter and setter
In your bean implement filterFunction, method need to have signature boolean (Object, Object, Locale) altough for this example only 1st argument (Object value) is used:
public boolean filterFunction(Object value, Object filter, Locale locale) {
// instanceof checking probably not needed
if (value == null || !(value instanceof String)) {
return true;
}
String valueInRow = (String)value;
// if nothing is selected, show row in table i.e. return true,
// you can play with this ofcourse
if (selectedItems == null || selectedItems.length == 0) {
return true;
}
// if item in row matches any of the items that were selected in header,
// show in table i.e. return true
for (int i = 0; i < selectedItems.length; i++) {
if (selectedItems[i].equals(valueInRow)) {
return true;
}
}
// if you don't want to show row in table, return false
return false;
}
In p:column, call filterfunction=#{someBean.filterFunction} - no arguments, no parenthesis, no need to use any filterMatchMode, leave only filterBy

PrimeFaces restore colomn visibility

I have a <p:datatable> in which there is some p:column and particularly one <p:columns>.
I'm also using one <p:columnToggler> to let user to display or hide column.
Memorize his choices is not difficult, it's to restore dynamically his choice which is a problem, especially for <p:columns> because there is no get/set for column inside <p:columns>. I'm also using a pagination and columntoggler+pagination are not friends.
What is the best way to restore users choices for a <p:dataTable> toggler ?
I use PrimeFaces 5.1.
Sorry i forgot to precise : i'm on primefaces 5.1
I found my solution !
In a first time, I put my p:columns inside a p:datatable which is inside a p:column and it's work ! The columntoggler change visibility of this whole column. So i can't hide column per column...
But i also use the pagination and columntoggler/pagination didn't work very well together... I can hide a column but if i change my page (using the paginator) in datatable, the column will re-appear empty ! But if i refresh the whole page, the paginator work correctly...
that's why i used this part of solution : Primefaces ColumnToggler does'nt work with pagination
Precisely a script to check/uncheck column visibility.
<script>
function updateToggles(widget){
$( widget.jqId + ' .ui-chkbox .ui-chkbox-box').each(function() {
var chkbox = $(this);
if(chkbox.hasClass('ui-state-active')) {
widget.check(chkbox);
} else {
widget.uncheck(chkbox);
}
});
}
</script>
<p:dataTable
id="datatable-factures"
value="#{listFactureHandler.table}"
var="facture"
label="Facture"
nativeElements="true"
paginator="true"
rowKey="#{facture.id}"
rowSelectMode="checkbox"
selection="#{listFactureHandler.selection}"
sortBy="#{facture.dateFacture.time}"
sortOrder="descending">
<f:facet name="header">
<p:commandButton id="toggler" type="button" value="Personnaliser" style="float:right;" />
<p:columnToggler widgetVar="columnToggler" id="columnToggler" datasource="datatable-factures" trigger="toggler" ><p:ajax event="toggle" listener="#{dsHandler.onToggleColumn}"/></p:columnToggler>
<p:ajax event="page" oncomplete="updateToggles(PF('columnToggler'))"/>
</f:facet>
<p:ajax event="toggleSelect" update=":formulaire:insert-actions" />
<p:ajax event="rowSelectCheckbox" update=":formulaire:insert-actions" />
<p:ajax event="rowUnselectCheckbox" update=":formulaire:insert-actions" />
<p:column selectionMode="multiple" styleClass="text-center" toggleable="false" />
[...]
<p:column id="columns-champs-perso" headerText="Champs Personnalisés" visible="false">
<p:dataTable id="subfacture" value="#{facture}">
<p:columns headerText="#{champs.libelle}" id="champspersos#{index}" value="#{listFactureHandler.dossier.champsPersonnalises}" columnIndexVar="index" var="champs">
<h:outputText value="#{facture.getChampPersonnalise(champs.code).valeur}" />
</p:columns>
</p:dataTable>
An handler (dsHandler) memorize datatable states.
On pre-rendering, i do something like that :
Map<String, Boolean> map = objlState.getColumnsVisibles();
for (String id : map.keySet()) {
Column objCol = (Column) FacesContext.getCurrentInstance().getViewRoot().findComponent(id);
if (objCol != null) {
objCol.setVisible(map.getOrDefault(id, true));
}
}
I memorize columns id, not indexes because columntoggler return columnIndex+visibility and did not pay attention to columns which are not rendered. (but datatable.getChildCount() return column number and take care of not rendered columns.
In hope that will help someone a day ;)

Resources