I have a web-app in JSF 2.1 + Primefaces 5.1. I have a datatable with selectable rows. When I select a row and press the Edit button, a dialog opens to allow the user to edit the values of that record. When the user presses the dialog's Save button, a procedure is called to update the row and update the datatable via Ajax. If the procedure fails due to some business logic requirements, the datatable shouldn't update, it should keep the old values for that row. But what happens is that the values get updated on client-side, although in the DB they remain with the old (and correct) values. After a refresh, the datatable shows the old values.
The datatable and the edit button:
<h:form id="search_users_form_id">
<p:commandButton id="edit_user_button_id" value="#{msgs.edit}" disabled="#{usersBean.editUserButtonDisabled}" onclick="openDialog('edit_user_dlg')" update=":edit_user_form_id" actionListener="#{usersBean.populateDataForUserEdition}"/>
<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" selection="#{usersBean.selectedUser}" selectionMode="single" emptyMessage="#{msgs.no_records}" sortBy="#{user.id}"
sortOrder="ascending" rows="15">
<f:facet name="header">
#{msgs.users}
</f:facet>
<p:ajax event="rowSelect" listener="#{usersBean.onRowSelect}" update=":search_users_form_id:edit_user_button_id"/>
<p:ajax event="rowUnselect" listener="#{usersBean.onRowUnselect}" update=":search_users_form_id:edit_user_button_id"/>
<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">
#{user.description}
</p:column>
<p:column headerText="#{msgs.password}" sortBy="#{user.password}" styleClass="centered-column">
#{user.password}
</p:column>
<p:column headerText="#{msgs.sending_system}" sortBy="#{user.playerIn.description}" styleClass="centered-column">
#{user.playerIn.description}
</p:column>
<p:column headerText="#{msgs.receiving_system}" sortBy="#{user.playerOut.description}" styleClass="centered-column">
#{user.playerOut.description}
</p:column>
<p:column headerText="#{msgs.active}" sortBy="#{user.active}" styleClass="centered-column">
<p:selectBooleanCheckbox value="#{user.active}" disabled="true"/>
</p:column>
<f:facet name="footer">
</f:facet>
</p:dataTable>
</h:form>
The dialog:
<p:dialog modal="true" appendTo="@(body)" header="#{msgs.edit_user}" widgetVar="edit_user_dlg" closeOnEscape="true" resizable="false">
<h:form id="edit_user_form_id">
<h:panelGrid columns="3">
#{msgs.description}: <p:inputText value="#{usersBean.selectedUser.description}"/>
<h:panelGroup layout="block">
<p:graphicImage id="edit_user_description_tooltip_image_id" library="images" name="icon-tooltip.jpg"/>
<p:tooltip id="edit_user_description_tooltip_id" for="edit_user_description_tooltip_image_id" value="#{msgs.tooltip_users_description}"/>
</h:panelGroup>
#{msgs.password}:
<p:inputText value="#{usersBean.selectedUser.password}" maxlength="50"/>
<h:panelGroup layout="block">
<p:graphicImage id="edit_user_password_tooltip_image_id" library="images" name="icon-tooltip.jpg"/>
<p:tooltip id="edit_user_password_tooltip_id" for="edit_user_password_tooltip_image_id" value="#{msgs.tooltip_users_password}"/>
</h:panelGroup>
#{msgs.sending_system}:
<p:selectOneMenu styleClass="edit-user-selectonemenu" id="user_sending_system_id" value="#{usersBean.selectedUser.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>
<h:panelGroup layout="block">
<p:graphicImage id="edit_user_sending_system_tooltip_image_id" library="images" name="icon-tooltip.jpg"/>
<p:tooltip id="edit_user_sending_system_tooltip_id" for="edit_user_sending_system_tooltip_image_id" value="#{msgs.tooltip_users_sending_system}"/>
</h:panelGroup>
#{msgs.receiving_system}:
<p:selectOneMenu styleClass="edit-user-selectonemenu" id="user_receiving_system_id" value="#{usersBean.selectedUser.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}"/>
</p:selectOneMenu>
<h:panelGroup layout="block">
<p:graphicImage id="edit_user_receiving_system_tooltip_image_id" library="images" name="icon-tooltip.jpg"/>
<p:tooltip id="edit_user_receiving_system_tooltip_id" for="edit_user_receiving_system_tooltip_image_id" value="#{msgs.tooltip_users_receiving_system}"/>
</h:panelGroup>
#{msgs.active}: <p:selectBooleanCheckbox id="edit_user_active_id" value="#{usersBean.selectedUser.active}"/>
<h:panelGroup layout="block">
<p:graphicImage id="edit_user_active_tooltip_image_id" library="images" name="icon-tooltip.jpg"/>
<p:tooltip id="edit_user_active_tooltip_id" for="edit_user_active_tooltip_image_id" value="#{msgs.tooltip_users_active}"/>
</h:panelGroup>
</h:panelGrid>
<h:panelGroup styleClass="edit-user-main-tooltip" layout="block">
<p:graphicImage id="edit_user_main_tooltip_image_id" library="images" name="icon-tooltip.jpg"/>
<p:tooltip id="edit_user_main_tooltip_id" for="edit_user_main_tooltip_image_id" value="#{msgs.tooltip_users_edit}"/>
</h:panelGroup>
<h:panelGroup styleClass="edit-user-dialog-buttons" layout="block">
<p:commandButton id="edit_user_save_button_id" value="#{msgs.save}" action="#{usersBean.updateUser}" onclick="closeDialog('edit_user_dlg')" update=":search_users_form_id:users_table_id"/>
<p:commandButton id="edit_user_cancel_button_id" value="#{msgs.cancel}" type="button" onclick="closeDialog('edit_user_dlg')"/>
</h:panelGroup>
</h:form>
</p:dialog>
The update method just takes the selectedUser object and passes it to a stored procedure.
public void updateUser() {
logger.entry();
this.selectedUser.setPlayerInId(this.selectedUser.getPlayerIn().getId());
this.selectedUser.setPlayerOutId(this.selectedUser.getPlayerOut().getId());
this.updateUser(this.selectedUser);
this.clearData();
logger.exit();
}
public void updateUser(DtoUser dtoUser) {
try {
logger.entry(dtoUser);
DtoBoUsers dtoBoUsers = dtoUser.toDtoBoUsers();
DtoTypeGenericResponse genericResponse = usersService.updateUser(backofficeUserBean.getCurrentSessionId(), dtoBoUsers);
if (genericResponse.getVResponse().equals(startupBean.getProperty(BackofficeProperties.RESPONSE_NOK))) {
BigDecimal id = this.selectedUser.getId();
logger.info(this.users.get(id.intValue()-1));
} else {
}
logger.exit(dtoUser);
} catch (BackofficeException | RuntimeException e) {
logger.error(ExceptionUtils.getStackTrace(e.fillInStackTrace()));
}
}
How can I solve this? Do I have to force a repopulate the datatable again with a query to the DB? Or do I have a cleaner way of doing it?
oncomplete="if (args.validationFailed) closeDialog('edit_user_dlg')"would be better. - ForguesRRequestContext.getCurrentInstance().addCallbackParam("validationFailed", true);but it didn't work. - BaderousaddCallbackParamcall withFacesContext.getCurrentInstance().validationFailed();. On Primefaces' User Guide (11.1) it is said that "By default validationFailed callback parameter is added implicitly if validation fails." - Baderous