3
votes

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?

1
Answer depends on how exactly you detect that the service method has failed while apparently staying in the same page. I expected to see a try-catch in bean action method, so you could handle it in the catch, but that's even absent. - BalusC
The DB procedure returns "OK" or "NOK" depending on the validations result. I have updated the initial post with the method that calls it. - Baderous
Do you really want to close the dialog if it fails? Maybe something like oncomplete="if (args.validationFailed) closeDialog('edit_user_dlg')" would be better. - ForguesR
I guess I have to write something else in addition. I tried RequestContext.getCurrentInstance().addCallbackParam("validationFailed", true); but it didn't work. - Baderous
I also replaced the addCallbackParam call with FacesContext.getCurrentInstance().validationFailed();. On Primefaces' User Guide (11.1) it is said that "By default validationFailed callback parameter is added implicitly if validation fails." - Baderous

1 Answers

5
votes

You basically want to perform update=":search_users_form_id:users_table_id" only when the business service call has succeeded. You could achieve that by removing that attribute from the <p:commandButton> and performing it programmatically in the action method with help of RequestContext#update().

try {
    // ...
    RequestContext.getCurrentInstance().update("search_users_form_id:users_table_id");
}
catch (Exception e) {
    // ...
}