How would i validate series selection in primefaces datatable - jsf

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

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

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.

Capturing primefaces <p:autoComplete> change event (detect emptying)

i got a question concerning an Primefaces autocomplete. Rigth now i am updating a field with the value of a property of the selected value in the autocomplete, like this:
This is in my xhtml:
<p:autoComplete
value="#{trFitoModel.selectedProducte}"
id="nomComercial"
completeMethod="#{trFitoBacking.completeProducte}"
var="producte" itemLabel="#{producte.nom}"
itemValue="#{producte}" converter="#{ProducteFitoConverter}"
forceSelection="true"
onkeyup="this.value = this.value.toUpperCase();">
<p:ajax event="itemSelect"
listener="#{trFitoBacking.handleSelect}"
update="text" global="false" />
<f:validator validatorId="qea.validators.EmptyFieldValidator" />
<f:attribute name="validationTitle" value=" NomComercial " />
</p:autoComplete>
</p:column>
<p:column>
<h:outputLabel>#{msgI18N.trFito}</h:outputLabel>
<h:outputText id="text"
value="#{traFitoBacking.resgistre}">
</h:outputText>
</p:column>
And this is my Backing Bean:
public void handleSelect(SelectEvent event) {
ProducteFitosanitari p=(ProducteFitosanitari)event.getObject();
setResgistre(Integer.toString(p.getNumRegistre()));
}
This works, but now I'm trying to update the outputText with id "text" with an empty String when the value of the autocomplete is empty.
How can I capture the event triggered when p:autoComplete is emptied?
Primefaces autoComplete generates 2 events: "change" and "itemSelect", for 2 methods of changing its content: typing or selecting from dropdown list. So you need to register 2 p:ajax listeners:
<p:autoComplete ... >
<p:ajax event="itemSelect" listener="#{bean.action}" process="#form"/>
<p:ajax event="change" listener="#{bean.action}" process="#form"/>
</p:autoComplete>
You'll need also 2nd server method signature:
public void action(AjaxBehaviorEvent event)
for capturing 'change' event.
Instead of event you can use onstart attribute with JavaScript to run... More options for p:ajax you'll find in Primefaces Users Guide, section "AjaxBehavior".

Updating entire <p:dataTable> on complete of <p:ajax event="cellEdit">

I am having difficulty re-rendering a PrimeFaces Datatable once a cell has been edited. Changing the value in one cell may change entries in the other cells, hence the need to refresh the entire table.
Here's the JSF page:
<h:form id="testForm">
<p:outputPanel id="testContainer">
<p:dataTable id="testTable" value="#{tableBean.data}" var="entry" editable="true" editMode="cell">
<p:ajax event="cellEdit" listener="#{tableBean.onCellEdit}" update=":testForm:testContainer" />
<p:column headerText="Col1">
<p:cellEditor>
<f:facet name="output"><h:outputText value="#{entry.col1}" /></f:facet>
<f:facet name="input"><p:inputText value="#{entry.col1}" /></f:facet>
</p:cellEditor>
</p:column>
<p:column headerText="Col2">
<p:cellEditor>
<f:facet name="output"><h:outputText value="#{entry.col2}" /></f:facet>
<f:facet name="input"><p:inputText value="#{entry.col2}" /></f:facet>
</p:cellEditor>
</p:column>
</p:dataTable>
<p:commandButton id="refreshButton" value="Redisplay" update="testContainer" />
</p:outputPanel>
</h:form>
And here's the backing bean:
#ManagedBean(name = "tableBean", eager = false)
#ViewScoped
public class TableBean {
public TableBean() {
RowData entry = new RowData("a1", "b1");
entries.add(entry);
entry = new RowData("a2", "b2");
entries.add(entry);
entry = new RowData("a3", "b3");
entries.add(entry);
}
public class RowData {
private String col1;
private String col2;
public RowData(String col1, String col2) {
this.col1 = col1;
this.col2 = col2;
}
public String getCol1() {
return col1;
}
public void setCol1(String col1) {
this.col1 = col1;
}
public String getCol2() {
return col2;
}
public void setCol2(String col2) {
this.col2 = col2;
}
}
private ArrayList<RowData> entries = new ArrayList<RowData>();
public List<RowData> getData() {
return entries;
}
public void onCellEdit(CellEditEvent event) {
entries.get(event.getRowIndex()).setCol1("Dummy Col 1");
entries.get(event.getRowIndex()).setCol2("Dummy Col 2");
}
}
When including update=":testForm:testContainer" within the cellEdit AJAX event, changing a cell value deletes the datatable on screen and only renders the cell content (along with the button) -- I do not understand why this is. When the update attribute is not specified, the table remains on screen with the active cell updated, but none of the other cells are updated (as to be expected).
The desired behaviour can be achieved (in a non-automated way) by not specifying the update attribute within the AJAX cellEdit event and clicking the Redisplay button after editing a cell's value. How can I achieve this in an automated way, and why does the update attribute not work as I expect?
I am using PrimeFaces 4.0.
The rowEdit and cellEdit events does by design inside the table not update/re-render anything else than the current row, even not when explicitly specified in update attribute. It's the consequence of PrimeFaces' a bit overzealous attempt to minimize the response size. This makes sense in most of the cases, but not in specifically your case. It's worth an issue report.
In the meanwhile, until they fix this behavior, your best bet is using <p:remoteCommand> to invoke the desired listener method and perform a full update of the table.
Rewrite
<p:dataTable ...>
<p:ajax event="cellEdit" listener="#{tableBean.onCellEdit}" update=":testForm:testContainer" />
...
</p:dataTable>
to
<p:remoteCommand name="onCellEdit" action="#{tableBean.onCellEdit}" update="testContainer" />
<p:dataTable ...>
<p:ajax event="cellEdit" oncomplete="onCellEdit()" />
...
</p:dataTable>
The BaLusC solution has not worked directly for me. The onCellEdit needs a CellEditEvent as param. My workaround is as following:
<p:remoteCommand name="onCellEdit" update="testContainer" />
<p:dataTable ...>
<p:ajax event="cellEdit" listener="#{tableBean.onCellEdit}" oncomplete="onCellEdit()" />
...
</p:dataTable>
If none of the solutions worked for you, this worked for me
<p:dataTable ... id="theId" widgetVar="theWidget" ...>
<p:ajax event="rowEdit" listener="#{...}"
oncomplete="PF('theWidget').filter()"/>
....
I'm calling the filter method on the PF widget on ajax complete, any method that does a "reload" of the table should work, I used filter because my table had column filters.
I tested your code. First I moved p:commandButton out of p:outputPanel. Here is modified code:
<h:form id="testForm">
<p:outputPanel id="testContainer">
<p:dataTable id="testTable" value="#{tableBean.data}" var="entry" editable="true" editMode="cell">
<p:ajax event="cellEdit" listener="#{tableBean.onCellEdit}" update=":testForm:testContainer" />
(...)
</p:dataTable>
</p:outputPanel>
<p:commandButton id="refreshButton" value="Redisplay" update="testContainer" />
</h:form>
I think this code doesn't work correctly. if you change anything in table, the p:ajax every time render full table. So, the program load basic data from TableBean constructor and deleted new data.
If I omit your p:ajax code there is not disapears any new data from screen. The refreshButton p:commandButton work correctly.
When including update=":testForm:testContainer" within the cellEdit
AJAX event, changing a cell value deletes the datatable on screen and
only renders the cell content (along with the button) -- I do not
understand why this is.
I think it is bad design add update=":testForm:testContainer" to ajax, because it's update your outputPanel more than as exepted (first time work correctly, second time couldn't edit cell, because the program update to many times table).
I don't know what is your goal. If you want render table without a commandButton, then could you specify one javascript event or p:message and this disappear you can render table.
I think if you omit update in p:ajax or specify update of one p:message, and move p.commandButton out of testContainer your code start work correctly.
After 5 years, this problem still exists. Unfortunately, while Baukes solution is extremly helpful and includes important insights it's still incomplete, as ltlBeBoy already pointed out in his comment. Subsequent edits without change lead to an inconsistent table state, where no more edits are possible. The reason is, that the oncomplete remote update comes after the edit mode of the new cell is already activated. So the edit mode of the new cell is destroyed by the update. However, the update can't be done in Ajax listener tableBean#onCellEdit, as this would display the table erroneously with one cell only.
The solution is, to execute the update in the remote commands listener and only, if a change happend. So, in tableBean you implement a programmatic update, a remote listener and a flag that indicates change:
public static void update(String id) {
PrimeFaces pf = PrimeFaces.current(); //RequestContext.getCurrentInstance() for <PF 6.2
if(pf.isAjaxRequest()) pf.ajax().update(id);
}
/** Whether onCellEdit changed the value */
boolean onCellEditChange;
public void onCellEditRemote() {
if(!onCellEditChange) update("testContainer");
}
public void onCellEdit(CellEditEvent event) {
... onCellEditChange= /*Change happend*/ ...
}
The remote command has no update attribute any more:
<p:remoteCommand name="onCellEdit" actionListener="#{tabelBean.onCellEditRemote}"/>
try using process
<p:remoteCommand name="onCellEdit" update="testContainer" process="#this" />
<p:dataTable ...>
<p:ajax event="cellEdit" listener="#{tableBean.onCellEdit}" oncomplete="onCellEdit()" process="#this" />
...
</p:dataTable>
This is my first post ever in here. As ltlBeBoy mentioned, BalusC's solution works only if the cell editing is done with an enter key hit.
None of the other suggestions listed in here worked for me.
In my case, I just wanted to update a specific row (that has each column's average) in the table on cellEdit event. I'm posting this in case if someone out there is looking for a solution: I managed to achieve this by separating that row in a second datatable right below the main one. Here's the code:
<pf:ajax event="cellEdit"
listener="#{newAgentMetricBacking.onCellEdit}"
update="c21413" />
<pf:dataTable id="c21413"
styleClass="noHeader"
value="">
<pf:column>
<h:outputText value="#{bundle.TeamAverage}"/>
</pf:column>
<pf:columns columnIndexVar="j"
value="#{newAgentMetricBacking.averageArray}"
var="avg">
<h:outputText value="#{newAgentMetricBacking.
averageArray[j]}"/>
</pf:columns>
</pf:dataTable>
Otherwise, as of today, I couldn't find a better solution for updating either the whole table or a specific row specifically upon success of cellEdit event.
Cheers!

primefaces datatable select all with paginator

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

Resources