Primefaces Datatable unable to select row after ajax update - jsf

I'm having what I can call a strange behavior.
I have a page with a panel with several drops downs for search purposes and a datatable in it to display the results.
By default when the page is loaded the values get initialized by a PostContruct init().
I can perform my search without any problem including updating the datatable.
Now, the part where it gets hard for me is the following:
I have a list of tickets with Open/Closed state. My search can be performed with either Open/Closed/ALL.
By default the datatable is loaded with only the open tickets and I can select them and redirect to another page using the selectedRow value.
When I choose to filter by Closed status and I click the button to search, the datatable gets updated I can select the rows but when I try to open any page I get a null pointer exception in the bean.
If I choose all, the datatable gets updated, I can select rows but I can only open the pages with the data that was showed in the first page load, so basically only the tickets that are in the open state. Those that are in Closed that I cannot open them
I'm using RequestScoped bean, I would like to have somehints what could be the problem for the selectRow not work with the new added data.
<ui:define name="content">
<h:form id="searchForm">
<p:panelGrid id="searchPanel" style="width: 1000px" styleClass="ticketsPanel">
<p:row>
<p:column>
<p:outputLabel for="lstStatus" value="Status"></p:outputLabel>
</p:column>
<p:column>
<p:selectOneMenu id="lstStatus" value="#{ticketController.searchStatus}">
<f:selectItem itemValue="Open" itemLabel="Open"></f:selectItem>
<f:selectItem itemValue="Closed" itemLabel="Closed"></f:selectItem>
<f:selectItem itemValue="ALL" itemLabel="ALL"></f:selectItem>
</p:selectOneMenu>
</p:column>
</p:row>
<f:facet name="footer">
<p:row>
<p:column colspan="6" style="alignment-adjust: middle">
<p:commandButton value="Search" action="#{ticketController.search()}" update=":searchForm:lstTicketsTable"></p:commandButton>
<p:commandButton value="Create" action="/tickets/createticket.xhtml?faces-redirect=true"></p:commandButton>
<p:commandButton value="View" action="#{ticketController.doUpdateTicket()}"></p:commandButton>
</p:column>
</p:row>
</f:facet>
</p:panelGrid>
<p:dataTable id="lstTicketsTable" selectionMode="single" paginator="true"
paginatorTemplate="{FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {CurrentPageReport} {RowsPerPageDropdown}"
rowsPerPageTemplate="10,15,50" rows="10" paginatorPosition="bottom"
value="#{ticketController.ticketList}" selection="#{ticketController.selectedTicket}" var="_tl" rowKey="#{_tl.number}">
<p:column headerText="Ticket NR">
<p:outputLabel value="#{_tl.number}"></p:outputLabel>
</p:column>
<p:column headerText="Summary">
<p:outputLabel value="#{_tl.summary}"></p:outputLabel>
</p:column>
<p:column headerText="Contact From">
<p:outputLabel value="#{_tl.contactfrom}"></p:outputLabel>
</p:column>
<p:column headerText="Researcher">
<p:outputLabel value="#{_tl.researcher.email == null ? '' : _tl.researcher.email}"></p:outputLabel>
</p:column>
<p:column headerText="NSN SR">
</p:column>
<p:column headerText="SR / CR">
</p:column>
<p:column headerText="Release">
<p:outputLabel value="#{_tl.release}"></p:outputLabel>
</p:column>
<p:column headerText="Status">
<p:outputLabel value="#{_tl.status}"></p:outputLabel>
</p:column>
<p:column headerText="Age">
<p:outputLabel value="#{_tl.startdate}"></p:outputLabel>
</p:column>
<p:column headerText="Priority">
<p:outputLabel value="#{_tl.priority}"></p:outputLabel>
</p:column>
</p:dataTable>
</h:form>
</ui:define>
And the backing bean functions doUpdateTicket is the one being called in the View button and the Search the one that updates the list of the datatable.
public void doUpdateTicket() {
FacesContext fc = FacesContext.getCurrentInstance();
ExternalContext ec = fc.getExternalContext();
try {
ec.redirect("/SupportSite/faces/tickets/updateticket.xhtml?tnr=" + this.selectedTicket.getNumber().toString());
} catch (IOException ex) {
Logger.getLogger(TicketController.class.getName()).log(Level.SEVERE, null, ex);
}
}
public void search() {
FacesContext fc = FacesContext.getCurrentInstance();
ExternalContext ec = fc.getExternalContext();
if (this.searchTicketNR != null) {
Ticket t = tjc.findTicket(Integer.parseInt(this.searchTicketNR));
if (t != null) {
try {
//This works when search by ticket number only
ec.redirect("/SupportSite/faces/tickets/updateticket.xhtml?tnr=" + t.getNumber().toString());
} catch (IOException ex) {
Logger.getLogger(TicketController.class.getName()).log(Level.SEVERE, null, ex);
}
}
} else if (this.getSearchStatus() != "ALL") {
setTicketList(tjc.findTicketByStatus(this.getSearchStatus()));
this.init();
} else {
setTicketList(tjc.findTicketEntities());
this.init();
}
}

Related

Ways to update p:dataTable after delete method?

I'm looking for ways to update p:dataTable after running delete method. In my case a user is expected to fill p:dataTable first with a parametrized search. Table contains rows that can be cancelled using delete method. This doesn't mean that they are deleted completely, delete method rather just changes some columns in db table. For example changes hasCancellationReference column to true. Now this means that I need the table still be shown on dataTable row, just with a changed status. Now I'm looking for the correct way to implement dataTable refresh after delete method.
My dataTable and delete button:
<h:form prependId="false" rendered="#{myController.lst ne null and myController.lst.size() gt 0}" id="dataTable">
<p:dataTable value="#{myController.lzy}" lazy="true" var="tra" rows="10" paginator="true" id="dtas"
selectionMode="single" selection="#{myController.cur}" rowKey="#{tra.transactionId}"
paginatorTemplate="{RowsPerPageDropdown} {FirstPageLink} {PreviousPageLink} {NextPageLink} {LastPageLink}"
paginatorPosition="top"
rowsPerPageTemplate="10,25,50">
<p:ajax event="rowSelect" listener="#{myController.receipt}" update="#{parent}:tradlg" process="#this" global="false" oncomplete="PF('tradlg').show()"/>
<p:ajax event="page" update=""/>
<p:column headerText="Timestamp" width="30">
<h:outputText value="#{path.toGet.myTimeVariable}">
<f:convertDateTime pattern="dd.MM.yyyy HH:mm:ss" timeZone="EET"/>
</h:outputText>
</p:column>
<p:column headerText="Line" width="10">
<h:outputText value="#{var.lineCode}"/>
</p:column>
<p:column headerText="Stop" width="60">
<h:outputText value="#{var.stopCode} #{var.stopName}" />
</p:column>
<p:column headerText="Vechicle" width="30">
<h:outputText value="#{var.vehicleNumber}"/>
</p:column>
<p:column headerText="Status" width="40">
<h:outputText value="Deleted" style="color: red" rendered="#{var.hasCancellationReference eq true}"/>
<p:commandButton value="Delete"
action="#{emvController.deleteTransaction(tra.transitAccount, tra.transactionId)}"
rendered="#{tra.hasCancellationReference eq false}"/>
</p:column>
</p:dataTable>
</h:form>
Things i have tried:
Running search method inside delete method in backend code:
public void deleteRow(int var, String varId) {
try {
MyParameterClass.getInstance().deleteRow(var, varId);
message = MyParameterClass.getInstance().getMessage();
if (message.equals("success 200")) {
successMsg();
//If delete was succesfull run searchMethod
searchMethod();
} else {
errorMsg();
System.out.println("Faces Message Controller: " + message);
}
} catch (Exception ex) {
Logger.getLogger(EmvController.class).error(ex);
return;
}
}
Doing plain refresh on page after delete method. This does work, but i suspect will cause problems with new features I plan to implement in the future.

How to dynamically disable/enable commandbutton in datatable in JSF/PrimeFaces

I am displaying list of some objects in primefaces datatable. Apart from object related columns, I have added two columns that contain commandbutton. Each of these commandbutton make ajax calls to desired methods in bean. I want to disable both commandbuttons in that row when any of the button is clicked and enable them again once action is completed successfully. The method execution is simulated by ajaxStatus which is working fine.
Following is selected code of xhtml
<h:form id="form1" >
<p:remoteCommand name="onload" action="#{controlBean.init}" autoRun="true" />
<h:outputtext>Sample Project</h:outputtext>
<p:ajaxStatus style="display:block;margin-bottom:2em;height:24px;">
<f:facet name="prestart">
<h:outputText value="Starting..." /> </f:facet>
<f:facet name="error"> <h:outputText value="Error" />
</f:facet>
<f:facet name="success"> <h:outputText value="Success" />
</f:facet>
<f:facet name="default"> <h:outputText value="Idle" />
</f:facet>
<f:facet name="start"> <h:outputText value="Please Wait" />
<p:graphicImage name="/images/ajaxloadingbar.gif" />
</f:facet>
</p:ajaxStatus>
<p:dataTable var="appl" id="tbl1" rowIndexVar="rowIndx" value="#{controlBean.applsList}">
<p:column headerText="Name">
<h:outputText value="#{appl.applName}" />
</p:column>
<p:column headerText="Type">
<h:outputText value="#{appl.applType}" />
</p:column>
<p:column headerText="Desc" rendered="true">
<h:outputText value="#{appl.applDesc}" />
</p:column>
<p:column headerText="Start Appl" id="col4">
<p:commandButton value="Submit" ajax="true"
process="#this"
widgetVar="startButtonVar"
id="startBtn" update="msgs, col4, startBtn"
action="#{controlBean.startAction}" style="margin-right:20px;"
styleClass="ui-priority-primary"
disabled="#{controlBean.startBtnDisabled}">
<f:setPropertyActionListener value="#{appl}" target="#{controlBean.selectedAppl}"/>
</p:commandButton>
</p:column>
<p:column headerText="Stop Appl" id="col5">
<p:commandButton value="Submit" ajax="true"
process="#this"
widgetVar="stopButtonVar"
id="stopBtn" update="msgs, col5"
action="#{controlBean.stopAction}" style="margin-right:20px;"
styleClass="ui-priority-primary"
disabled="#{controlBean.btnDisabled}">
<f:setPropertyActionListener value="#{appl}" target="#{controlBean.selectedAppl}"/>
</p:commandButton>
</p:column>
</p:dataTable>
</h:form>
backing bean methods are as under:
public void startAction()
{
System.out.println("In start action method");
this.btnDisabled = true;
for(int i=0;i<100000;i++)
{
if(i%10000 == 0)
{
System.out.println(i);
}
for(int j=0;j<10000;j++)
{
for(int k=0;k<1000;k++)
{
}
}
}
MessageUtils.info(getSelectedAppl().getApplName()+" has been started");
this.btnDisabled = false;
}
public void stopAction()
{
System.out.println("In stop action method");
this.btnDisabled = true;
for(int i=0;i<100000;i++)
{
if(i%10000 == 0)
{
System.out.println(i);
}
for(int j=0;j<10000;j++)
{
for(int k=0;k<1000;k++)
{
}
}
}
MessageUtils.info(getSelectedAppl().getApplName()+" has been stopped");
this.btnDisabled = false;
}
Everything is working fine except disabling/enabling of commandbuttons. Any help in this regard will be highly appreciated.
Regards
You could disable them on the onclick javascript handler:
<p:commandButton id="startBtn"
widgetVar="startButtonVar"
onclick="PF('startButtonVar').disable();"
.....
The onclick will be executed BEFORE the AJAX call. On return, as the button is updated, it will be reenabled.

p:dataTable filtered, sorted, paginated results get displayed in a single row

I'm working on a theme for an app I'm making with PrimeFaces 6.2 (community edition) and I'd like to get my simulated DAO objects working before I proceed with my css templating.
I've got an issue I came across in the past and I can't find the right answer for it again. Would someone point out an error I've made somewhere in my code?
Details:
I've made a somewhat complex DataTable using PrimeFaces LazyDataModel with little help from PrimeFaces Showcase pages. My main issue is, when I write something in the filter fields or click on any column headers to do data sorting or even click on pagination buttons I get an unexpexted data rendering issue.
Filtered, sorted and paginated results get displayed in a single concatenated row what I don't want. I've posted images and code further below for insight.
Also, I'd like to point out:
No exceptions in JS console.
No exceptions in Java console.
don't mind the missing pagination icons (text-indent: 0;)
XHTML:
<h:form id="input-form-dt2">
<H4>DATA TABLE - LAZY MODEL</H4>
<div class="flex-container">
<p:outputPanel id="dev-input-panel-13">
<p:dataTable var="DOTable" value="#{dtModelLazy.DOTList}" paginator="true" rows="10" rowKey="#{DOTable.userID}"
paginatorTemplate="{RowsPerPageDropdown} {FirstPageLink} {PreviousPageLink} {CurrentPageReport} {NextPageLink} {LastPageLink}"
rowsPerPageTemplate="5,10,15,25,50" selectionMode="single" selection="#{dtModelLazy.selectedObj}" id="DTModelB" lazy="true">
<p:ajax event="rowSelect" listener="#{dtModelLazy.onRowSelect}" update="input-form-dt2:dlgDTOObjDetail" oncomplete="PF('DTObjDialog').show()" />
<p:column headerText="User ID" sortBy="#{DOTable.userID}" filterBy="#{DOTable.userID}" filterMatchMode="contains">
<h:outputText value="#{DOTable.userID}" />
</p:column>
<p:column headerText="Name" sortBy="#{DOTable.name}" filterBy="#{DOTable.name}" filterMatchMode="contains">
<h:outputText value="#{DOTable.name}" />
</p:column>
<p:column headerText="Surname" sortBy="#{DOTable.surname}" filterBy="#{DOTable.surname}" filterMatchMode="contains">
<h:outputText value="#{DOTable.surname}" />
</p:column>
<p:column headerText="Age" sortBy="#{DOTable.age}" filterBy="#{DOTable.age}" filterMatchMode="contains">
<h:outputText value="#{DOTable.age}" />
</p:column>
<p:column headerText="Address" sortBy="#{DOTable.address}" filterBy="#{DOTable.address}" filterMatchMode="contains">
<h:outputText value="#{DOTable.address}" />
</p:column>
<p:column headerText="City" sortBy="#{DOTable.city}" filterBy="#{DOTable.city}" filterMatchMode="contains">
<h:outputText value="#{DOTable.city}" />
</p:column>
<p:column headerText="Post code" sortBy="#{DOTable.postCode}" filterBy="#{DOTable.postCode}" filterMatchMode="contains">
<h:outputText value="#{DOTable.postCode}" />
</p:column>
<p:column headerText="Country code" sortBy="#{DOTable.countryCode}" filterBy="#{DOTable.countryCode}" filterMatchMode="contains">
<h:outputText value="#{DOTable.countryCode}" />
</p:column>
<p:column headerText="Phone number" sortBy="#{DOTable.phoneNumber}" filterBy="#{DOTable.phoneNumber}" filterMatchMode="contains">
<h:outputText value="#{DOTable.phoneNumber}" />
</p:column>
<p:column headerText="Avatar hash" sortBy="#{DOTable.photoID}" filterBy="#{DOTable.photoID}" filterMatchMode="contains">
<h:outputText value="#{DOTable.photoID}" />
</p:column>
</p:dataTable>
<p:dialog id="dlgDTOObjDetail" header="DataTable Object Detail" widgetVar="DTObjDialog" modal="true" showEffect="fade" hideEffect="fade" resizable="false">
<p:outputPanel id="DTObjDetail">
<p:panelGrid columns="2" rendered="#{not empty dtModelLazy.selectedObj}" columnClasses="label,value">
<h:outputText value="User ID: " />
<h:outputText value="#{dtModelLazy.selectedObj.userID}" />
<h:outputText value="Name: " />
<h:outputText value="#{dtModelLazy.selectedObj.name}" />
<h:outputText value="Surname: " />
<h:outputText value="#{dtModelLazy.selectedObj.surname}" />
<h:outputText value="Age: " />
<h:outputText value="#{dtModelLazy.selectedObj.age}" />
<h:outputText value="Address: " />
<h:outputText value="#{dtModelLazy.selectedObj.address}" />
<h:outputText value="City: " />
<h:outputText value="#{dtModelLazy.selectedObj.city}" />
<h:outputText value="Post code: " />
<h:outputText value="#{dtModelLazy.selectedObj.postCode}" />
<h:outputText value="Country code: " />
<h:outputText value="#{dtModelLazy.selectedObj.countryCode}" />
<h:outputText value="Phone number: " />
<h:outputText value="#{dtModelLazy.selectedObj.phoneNumber}" />
<h:outputText value="Photo hash: " />
<h:outputText value="#{dtModelLazy.selectedObj.photoID}" />
</p:panelGrid>
</p:outputPanel>
</p:dialog>
</p:outputPanel>
</div>
<hr></hr>
</h:form>
LAZY MODEL:
public class DataTableModelLazy extends LazyDataModel<DODataTable> {
private static final long serialVersionUID = -2647349397077805782L;
private List<DODataTable> datasource;
public DataTableModelLazy(List<DODataTable> datasource) {
this.datasource = datasource;
}
#Override
public DODataTable getRowData(String rowKey) {
for(DODataTable dtObj : datasource) {
if(dtObj.getUserID().equals(rowKey))
return dtObj;
}
return null;
}
#Override
public Object getRowKey(DODataTable dtObj) {
return dtObj.getUserID();
}
#Override
public List<DODataTable> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String,Object> filters) {
List<DODataTable> data = new ArrayList<DODataTable>();
//filter
for(DODataTable dtObj : datasource) {
boolean match = true;
if(filters != null) {
for (Iterator<String> it = filters.keySet().iterator(); it.hasNext();) {
try {
String filterProperty = it.next();
Object filterValue = filters.get(filterProperty);
Field field = dtObj.getClass().getDeclaredField(filterProperty);
field.setAccessible(true);
String fieldValue = String.valueOf(field.get(dtObj));
field.setAccessible(false);
if(filterValue == null || fieldValue.startsWith(filterValue.toString())) {
match = true;
} else {
match = false;
break;
}
} catch(Exception e) {
match = false;
}
}
}
if(match) {
data.add(dtObj);
}
}
//sort
if(sortField != null) {
Collections.sort(data, new DataTableModelLazySorter(sortField, sortOrder));
}
//rowCount
int dataSize = data.size();
this.setRowCount(dataSize);
//paginate
if(dataSize > pageSize) {
try {
return data.subList(first, first + pageSize);
} catch(IndexOutOfBoundsException e) {
return data.subList(first, first + (dataSize % pageSize));
}
} else {
return data;
}
}
}
VIEW BEAN:
#Named("dtModelLazy")
#ViewScoped
public class DataGeneratorBeanLazy implements Serializable {
private static final long serialVersionUID = -5918527333909822277L;
private LazyDataModel<DODataTable> DOTList;
private DODataTable selectedObj;
#Inject
private DataGeneratorBean dataGen;
#PostConstruct
public void init() {
DOTList = new DataTableModelLazy(dataGen.createDTObjects(1500));
}
public LazyDataModel<DODataTable> getDOTList() {
return DOTList;
}
public void setDOTList(LazyDataModel<DODataTable> dOTList) {
DOTList = dOTList;
}
public void onRowSelect(SelectEvent event) {
FacesMessage msg = new FacesMessage("DataTable object selected!", ((DODataTable) event.getObject()).getUserID());
FacesContext.getCurrentInstance().addMessage(null, msg);
}
public DODataTable getSelectedObj() {
return selectedObj;
}
public void setSelectedObj(DODataTable selectedObj) {
this.selectedObj = selectedObj;
}
}
Update 1
I have modified the update property as update="input-form-dt2:dlgDTOObjDetail"to meet the suggestion provided. Also, I added the id property for the dialog. The issue still remains.
Update 2
I've changed my approach and started with the basic DataTable first. Also, I've stripped the .xhtml to a bare minimum. It contains only a form with the DataTable inside like so:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:p="http://primefaces.org/ui">
<div>
UI components design
</div>
<h:form id="input-form-dt1">
<h4>DATA TABLE - BASIC</h4>
<p:dataTable id="DTableA" var="dataObject" value="#{dataTableBean.objectList}" paginator="true" rows="10" rowKey="#{dataObject.id}"
paginatorTemplate="{RowsPerPageDropdown} {FirstPageLink} {PreviousPageLink} {CurrentPageReport} {NextPageLink} {LastPageLink}"
rowsPerPageTemplate="5,10,15,25,50">
<p:column headerText="User ID" sortBy="#{dataObject.userID}" filterBy="#{dataObject.userID}" filterMatchMode="contains">
<h:outputText value="#{dataObject.userID}" />
</p:column>
<p:column headerText="Name" sortBy="#{dataObject.name}" filterBy="#{dataObject.name}" filterMatchMode="contains" >
<h:outputText value="#{dataObject.name}" />
</p:column>
<p:column headerText="Surname" sortBy="#{dataObject.surname}" filterBy="#{dataObject.surname}" filterMatchMode="contains" >
<h:outputText value="#{dataObject.surname}" />
</p:column>
<p:column headerText="Age" sortBy="#{dataObject.age}" filterBy="#{dataObject.age}" filterMatchMode="contains" >
<h:outputText value="#{dataObject.age}" />
</p:column>
<p:column headerText="Address" sortBy="#{dataObject.address}" filterBy="#{dataObject.address}" filterMatchMode="contains" >
<h:outputText value="#{dataObject.address}" />
</p:column>
<p:column headerText="City" sortBy="#{dataObject.city}" filterBy="#{dataObject.city}" filterMatchMode="contains" >
<h:outputText value="#{dataObject.city}" />
</p:column>
<p:column headerText="Post code" sortBy="#{dataObject.postCode}" filterBy="#{dataObject.postCode}" filterMatchMode="contains" >
<h:outputText value="#{DOTable.postCode}" />
</p:column>
<p:column headerText="Country code" sortBy="#{dataObject.countryCode}" filterBy="#{dataObject.countryCode}" filterMatchMode="contains" >
<h:outputText value="#{dataObject.countryCode}" />
</p:column>
<p:column headerText="Phone number" sortBy="#{dataObject.phoneNumber}" filterBy="#{dataObject.phoneNumber}" filterMatchMode="contains" >
<h:outputText value="#{dataObject.phoneNumber}" />
</p:column>
<p:column headerText="Avatar hash" sortBy="#{dataObject.photoID}" filterBy="#{dataObject.photoID}" filterMatchMode="contains">
<h:outputText value="#{dataObject.photoID}" />
</p:column>
</p:dataTable>
</h:form>
</ui:composition>
As you can see I've also removed all event listeners. I've added a new field to my data object (id of type Integer) and bound DataTables rowKey to it (previously bound to userID of type String - not a good idea). My DataTable backing bean is now as basic as it can be:
#Named("dataTableBean")
#ViewScoped
public class DataTableBean implements Serializable {
private static final long serialVersionUID = -1662465661106141910L;
private List<DTObject> objectList;
#Inject
private DataGeneratorBean dataGen;
#PostConstruct
public void init() {
setObjectList(dataGen.createDTObjects(1500));
}
public List<DTObject> getObjectList() {
if (objectList == null) {
return new ArrayList<>();
} else {
return objectList;
}
}
public void setObjectList(List<DTObject> objectList) {
this.objectList = objectList;
}
}
Now, there are no custom filters, sorters or paginators of any type, not even a data model, just raw data objects in a simple list. The output result is exactly the same as before when paginator buttons are clicked or data gets filtered. All resulting data still gets displayed in a single concatenated line.
ANSWER:
As Kukeltje pointed out in the comments I've made a complete nonsense in my main container and added an autoupdate component to it. That component messed up my data table events, loading data without a table to hold it. Once I've removed the component from my main container, everything worked out. Here is the code for my main container (commented out the troublemaker).
<div id="content-window">
<p:outputPanel id="content-panel">
<ui:include src="#{contentLoaderBean.mainContent}" />
<!-- <p:autoUpdate /> -->
</p:outputPanel>
</div>
The only situations I've seen this happening is when the datatable is fully updated in addtion to a partial update of the datatable via an event (page or filter or sort or...) In the rowSelect you do seem to update the full form which contains the datatable as well. That is bad practice and can as mentioned result in what you seem so remove that.
But...in your question there are no filter ajax events that explicitly update the full datatable so that cannot cause it. Yet with 99% vertainty there is something fully updates the datatable. Three options
there is something in your live code you left out of the datatable in your question
There is something else outside the code you posted that plays havoc (an autoupdate e.g),
an update from the server side is being done in a method

Invalidate Primefaces's datatable row on row edition

I'm developing a web-app with JSF 2.1 + Primefaces 5.1. I have a datatable with row edition and each row has 2 inputText, 2 selectOneMenu and 1 selectBooleanCheckbox. I want to perform some business logic validation when clicking the check icon: it will validate the values of the 2 selectOneMenus. For that, I created a custom validator associated with the second selectOneMenu that validates both values:
<p:dataTable id="users_table_id" var="user" value="#{usersBean.users}" rowKey="#{user.id}" paginator="true"
paginatorTemplate="{CurrentPageReport} {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
rowsPerPageTemplate="5,10,15" emptyMessage="#{msgs.no_records}" sortBy="#{user.id}" sortOrder="ascending" rows="15" editable="true">
<f:facet name="header">
#{msgs.users}
</f:facet>
<p:ajax event="rowEdit" listener="#{usersBean.onRowEdit}" update=":users_form_id:growl"/>
<p:ajax event="rowEditInit" listener="#{usersBean.onRowEditInit}" update=":users_form_id:growl"/>
<p:ajax event="rowEditCancel" listener="#{usersBean.onRowEditCancel}" update=":users_form_id:growl"/>
<p:column headerText="#{msgs.id}" sortBy="#{user.id}" styleClass="centered-column">
#{user.id}
</p:column>
<p:column headerText="#{msgs.description}" sortBy="#{user.description}" styleClass="centered-column">
<p:cellEditor>
<f:facet name="output">#{user.description}</f:facet>
<f:facet name="input"><p:inputText value="#{user.description}" styleClass="editable-cell"/></f:facet>
</p:cellEditor>
</p:column>
<p:column headerText="#{msgs.password}" sortBy="#{user.password}" styleClass="centered-column">
<p:cellEditor>
<f:facet name="output">#{user.password}</f:facet>
<f:facet name="input"><p:inputText value="#{user.password}" styleClass="editable-cell"/></f:facet>
</p:cellEditor>
</p:column>
<p:column headerText="#{msgs.sending_system}" sortBy="#{user.playerIn.description}" styleClass="centered-column">
<p:cellEditor>
<f:facet name="output">#{user.playerIn.description}</f:facet>
<f:facet name="input">
<p:selectOneMenu binding="#{userPlayerInComponent}" styleClass="editable-cell" id="users_table_sending_system_id" value="#{user.playerIn}" effectSpeed="fast" filter="true" filterMatchMode="contains" converter="#{playerConverter}">
<f:selectItem itemLabel="#{msgs.select_option}" itemValue="#{null}" noSelectionOption="true"/>
<f:selectItems value="#{usersBean.players}" var="player" itemValue="#{player}" itemLabel="#{player.description}"/>
</p:selectOneMenu>
</f:facet>
</p:cellEditor>
</p:column>
<p:column headerText="#{msgs.receiving_system}" sortBy="#{user.playerOut.description}" styleClass="centered-column">
<p:cellEditor>
<f:facet name="output">#{user.playerOut.description}</f:facet>
<f:facet name="input">
<p:selectOneMenu styleClass="editable-cell" id="users_table_receiving_system_id" value="#{user.playerOut}" effectSpeed="fast" filter="true" filterMatchMode="contains" converter="#{playerConverter}">
<f:selectItem itemLabel="#{msgs.select_option}" itemValue="#{null}" noSelectionOption="true"/>
<f:selectItems value="#{usersBean.players}" var="player" itemValue="#{player}" itemLabel="#{player.description}"/>
<f:validator validatorId="userValidator"/>
<f:attribute name="userPlayerInComponent" value="#{userPlayerInComponent}"/>
</p:selectOneMenu>
</f:facet>
</p:cellEditor>
</p:column>
<p:column headerText="#{msgs.active}" sortBy="#{user.active}" styleClass="centered-column">
<p:cellEditor>
<f:facet name="output"><p:selectBooleanCheckbox value="#{user.active}" disabled="true"/></f:facet>
<f:facet name="input"><p:selectBooleanCheckbox value="#{user.active}"/></f:facet>
</p:cellEditor>
</p:column>
<p:column styleClass="datatable-row-editor">
<p:rowEditor/>
</p:column>
<f:facet name="footer">
</f:facet>
</p:dataTable>
#FacesValidator("userValidator")
public class UserValidator implements Validator {
private static final Logger logger = LogManager.getLogger(UserValidator.class);
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
logger.entry(context, component, value);
if (value == null) {
return; // Let required="true" handle.
}
UIInput userPlayerInComponent = (UIInput)component.getAttributes().get("userPlayerInComponent");
if (!userPlayerInComponent.isValid()) {
return; // Already invalidated. Don't care about it then.
}
DtoPlayer playerIn = (DtoPlayer)userPlayerInComponent.getValue();
if (playerIn == null) {
return; // Let required="true" handle.
}
DtoPlayer playerOut = (DtoPlayer)value;
UsersBean usersBean = context.getApplication().evaluateExpressionGet(context, "#{usersBean}", UsersBean.class);
BigDecimal userId = usersBean.getUserEditingId();
logger.info(userId + " - " + playerIn.getDescription() + " - " + playerOut.getDescription());
for (DtoUser dtoUser : usersBean.getUsers()) {
if (!userId.equals(dtoUser.getId()) && dtoUser.getPlayerIn().equals(playerIn) && dtoUser.getPlayerOut().equals(playerOut)) {
logger.info("Invalidating...");
userPlayerInComponent.setValid(false);
FacesContext.getCurrentInstance().validationFailed();
throw new ValidatorException(new FacesMessage("BOOM!"));
}
}
logger.exit();
}
}
I was expecting that, when validation fails (and a ValidatorException is thrown), the datatable row would stay in edit mode and the two selectOneMenus highlighted with red colour. But what happens is that the row exits from edit mode, keeping the old values. If I edit it again, it displays the submitted values and the invalid cells are marked with red. And this behaviour continues as long as I try to submit the invalid values. If I close the edit mode, the invalid values are discarded and the row keeps only the old values (whether in view or edit mode).
How can I solve this? From the Primefaces' showcase, one can see that when you try to submit, let's say, a string with alfabetic characters in the year cell, the row keeps the edit mode and is highlighted. I want to achieve the same behaviour.

p:dialog shows empty values when when I go to the next page of the p:datatable using paginator

I have been using onEdit in the p:datatable and it works fine.My data table has a pagintor and shows 5 records per page.so when I click on the onEdit in the first page event.getObject gets the changed value in the bean.Now when I go the next page the event.getObject does not work and returns the old value only.
Same with the dialog box.In the table I have a link when clicked opens a dialog box in whcih I am populating few fields with the values from the row selected.It works fine in the first page and When I navigate to other pages gives empty values.
Here is my jsf code:
<p:dataTable value="#{mybean.userList}"
var="item"
id="dataTab"
widgetVar="usersTable"
tableStyleClass="data" paginator="true" rows="5"
filteredValue="#{userController.filteredUsers}"
editable="true"
rowKey="#{item}"
>
<p:ajax event="rowEdit" listener="#{mybean.onEdit}" update=":userForm:growl" />
<p:ajax event="rowEditCancel" listener="#{mybean.onCancel}" update=":userForm:growl" />
<f:facet name="header">
<p:outputPanel>
<h:outputText value="Search all fields:" />
<p:inputText id="globalFilter" onkeyup="('usersTable').filter()" style="width:150px" />
</p:outputPanel>
</f:facet>
<p:column sortBy="#{item.firstName}" filterBy="#{item.firstName}"
filterMatchMode="startsWith">
<p:cellEditor>
<f:facet name="header">
<h:outputText value="First Name" />
</f:facet>
<f:facet name="output">
<h:outputText value="#{item.firstName}" />
</f:facet>
<f:facet name="input">
<p:inputText value="#{item.firstName}" style="width:100%"/>
</f:facet>
</p:cellEditor>
</p:column>
<p:column sortBy="#{item.lastName}" filterBy="#{item.lastName}" filterMatchMode="startsWith">
<p:cellEditor>
<f:facet name="header">
<h:outputText value="Last Name" />
</f:facet>
<p:column headerText="Update" style="width:6%">
<p:rowEditor />
</p:column>
</p:dataTable>
<p:dialog id="modalDialog"
header="Delete User?"
modal="true"
resizable="false"
draggable="false"
widgetVar="delUserConf"
>
<h:panelGrid id="display" columns="2" style="width: 150px;">
<h:outputLabel value="Firat Name" style="font-weight: bold"/>
<h:outputText value="Last Name" style="border: none"/>
</h:panelGrid>
</p:dialog>
Here is the code in my bean for the edit functionality:
public String onEdit(RowEditEvent event)
{
User user=(User)event.getObject());
user.getFirstName();
}
I did not add the link that I have in my form which pops up the dialog.Also I read that this might be because of Lazy loading and I am not sure about it.I am populating my table with a list and not the model.
Could you let me know what should I do to make it work?
maybe you should switch to SessionBean scope because the scope view is released when there are changes in the view. Try SessionBean. Also you can call from view scope any sessionbean like this, where controladorMB is a sessionbean
public void detallesPersona() {
try {
FacesContext ctx = FacesContext.getCurrentInstance();
ValueExpression vex = ctx.getApplication().getExpressionFactory().createValueExpression(ctx.getELContext(), "#{controladorMB}", ControladorMB.class);
ControladorMB gMB = (ControladorMB) vex.getValue(ctx.getELContext());
this.setSelectedPersona(gMB.getPersonaBean().detallesPersonas(this.selectedPersona.getIdPersona()));
} catch (Exception e) {
JsfUtil.addErrorMessage(e, "Error: " + e.getMessage());
}
}

Resources