Search code examples
jsfprimefaces

Gets random dataTable selection when I cancel master-details dialog


I have a basic selection DataTable with a list of emails and a SessionScope bean. When I edit an email, a dialog box appears with the email to be edited. The first one I select is the correct email, but when I select another email, a random email appears instead. I don't understand why this is happening. I have checked the official Primefaces 5 documentation and tried all the methods mentioned in the code, as well as just using an action and passing the email directly in the action. The thing is, when I cancel the dialog box, the next selected email and all others except the first one are random emails. Thank you for the help.

My xhtml:

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
    xmlns:p="http://primefaces.org/ui"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:pe="http://primefaces.org/ui/extensions"
    xmlns:c="http://java.sun.com/jsp/jstl/core"
    template="/facelets/template.xhtml">

    <ui:define name="headpage">
        <ui:include src="/facelets/head.xhtml"></ui:include>
    </ui:define>

    <ui:define name="bodypage">
        <f:event type="preRenderView" listener="#{emailBean.cargaListaEmail}" />
        <f:event
            listener="#{busquedaSugerenciasExpedientesBean.resetCajita()}"
            type="preRenderView" />

        <pe:importConstants
            className="es.ja.csalud.sas.intranet.buzoncontacto.constantes.NombreAtributos"
            var="nombreAtributos" />
        <pe:importConstants
            className="es.ja.csalud.sas.intranet.buzoncontacto.constantes.Cadenas"
            var="cadenas" />
        <pe:importConstants
            className="es.ja.csalud.sas.intranet.buzoncontacto.constantes.NavigationRules"
            var="navigationRules" />
        <div class="container">
            <div class="cajaConfiguracion">
                <h:form id="formApertura" styleClass="formApertura">
                    <div class="cabeza">
                        <h2 class="pageTitle">#{lbl.configuracion_titulo_emails}</h2>
                    </div>

                    <p:accordionPanel multiple="true" activeIndex="0" id="acordeon">
                        <p:tab title="#{lbl.configuracion_titulo_emails}">

                            <div class="fluido anchoCompleto">
                                <p:dataTable id="tablaEmails" var="email"
                                    styleClass="dt_configuracion"
                                    tableStyle="table-layout: auto; width:100%" widgetVar="email"
                                    value="#{emailBean.listaEmails}" editable="true"
                                    sortBy="#{email.nombreEmail}" rows="10" paginator="true"
                                    paginatorTemplate="{PreviousPageLink} {PageLinks} {NextPageLink} {CurrentPageReport} {RowsPerPageDropdown}"
                                    currentPageReportTemplate="{currentPage} DE {totalPages}"
                                    rowsPerPageLabel="Mostrar"
                                    rowsPerPageTemplate="5,10,50,100,{ShowAll|'Todo'}"
                                    paginatorPosition="bottom" rowKey="#{email.idEmail}"
                                    emptyMessage="#{msg.tbl_noRegist}"
                                    >
                                    
                                    <p:ajax event="rowSelect" 
                                            listener="#{grupoBean.onRowSelect}" update="@this" />
                                    
                                    <p:column headerText="#{lbl.nombre_email}"
                                        sortBy="#{email.nombreEmail}">
                                        <h:outputText value="#{email.nombreEmail}" />
                                    </p:column>
                                    <p:column headerText="#{lbl.editor}"
                                        sortBy="#{email.modificador}">
                                        <h:outputText value="#{email.modificador}" />
                                    </p:column>
                                    <p:column headerText="#{lbl.f_modificado}"
                                        sortBy="#{email.fechaModificacion}">
                                        <h:outputText value="#{email.fechaModificacion.toDate()}">
                                            <f:convertDateTime pattern="dd/MM/yyyy HH:mm:ss" />
                                        </h:outputText>
                                    </p:column>
                                    <p:column headerText="#{lbl.activo}" sortBy="#{email.activo}">
                                        <h:outputText value="#{email.activo ? 'Si' : 'No'}" />
                                    </p:column>

                                    <p:column styleClass="enlaceEdicion accionConfiguracion"
                                        headerText="#{lbl.list_acciones}">
                                        <h:outputText value="" />
                                        <p:commandButton value="#{lbl.bt_editar}"
                                                         title="#{lbl.bt_editar}"
                                                         icon="fa fa-edit"
                                                         action="#{emailBean.editarEmail()}"
                                                         process="@this"
                                                         update=":formApertura:acordeon:dialogAnadirEmail"
                                                         oncomplete="PF('dialogAnadirEmail').show();"
                                                         >
<!--                                            <p:ajax event="click" listener="#{emailBean.setEmailSeleccionada(email)}" update="@none" /> -->
<!--                                            <p:confirm header="Editar Email" -->
<!--                                                       message="¿Está seguro que desea editar #{email.nombreEmail}?" -->
<!--                                                       icon="ui-icon-alert" /> -->
<!--                                            <f:setPropertyActionListener value="#{email}" -->
<!--                                                  target="#{emailBean.emailSeleccionada}"/> -->
                                                <f:actionListener
                                                binding="#{emailBean.setEmailSeleccionada(email)}" />
                                        </p:commandButton>



                                        <p:commandButton value="#{lbl.bt_eliminar}" id="btnEliminar"
                                            title="#{lbl.bt_eliminar}" icon="far fa-trash-alt"
                                            action="#{emailBean.borrarEmail()}" process="@this"
                                            update=":msgs :formApertura:acordeon:tablaEmails">
                                            <f:setPropertyActionListener value="#{email}"
                                                target="#{emailBean.emailSeleccionada}" />
                                            <p:confirm header="Eliminar Email"
                                                message="¿Está seguro que desea eliminar #{email.nombreEmail}?"
                                                icon="ui-icon-alert" />

                                        </p:commandButton>

                                    </p:column>
                                </p:dataTable>
                            </div>
                            <!-- DIALOGO PARA AÑADIR/EDITAR TIPO DE GESTION MASIVA -->
                            <p:dialog header="#{lbl.email_configuracion}"
                                widgetVar="dialogAnadirEmail" id="dialogAnadirEmail"
                                modal="true" minimizable="false" maximizable="false"
                                closable="true" styleClass="modal1Filas1Campos"
                                resizable="false">
                                <h:panelGroup id="panel">
                                    <div class="grupoFiltrosAlineados">
                                        <div class="classFiltrosAlineados">
                                            <h:outputLabel class="labelFiltrosAlineados labelEstilo"
                                                value="#{lbl.nombre_email}" />
                                            <p:inputText styleClass="uiFiltrosAlineados"
                                                label="#{lbl.nombre_email}" required="true"
                                                value="#{emailBean.emailEditar.nombreEmail}">
                                                <f:validateLength maximum="200" />
                                                <f:validator validatorId="com.emailValidator" />
                                            </p:inputText>
                                        </div>


                                        <div class="classFiltrosAlineados">
                                            <h:outputLabel class="labelFiltrosAlineados labelEstilo"
                                                value="#{lbl.activo} " />
                                            <p:selectBooleanCheckbox
                                                styleClass="botonCheckbox labelCheck checkMargen"
                                                value="#{emailBean.emailEditar.activo}">
                                                <p:ajax event="change" process="@this" update="@this" />
                                            </p:selectBooleanCheckbox>
                                        </div>

                                    </div>
                                </h:panelGroup>

                                <br />

                                <p:commandButton styleClass="botonOtros"
                                    value="#{lbl.bt_aceptar}" process="@this dialogAnadirEmail"
                                    action="#{emailBean.saveEmail()}"
                                    update=":msgs formApertura:acordeon:tablaEmails">
                                </p:commandButton>

                                <p:commandButton styleClass="botonOtros"
                                    value="#{lbl.bt_cancelar}" type="button"
                                    action="#{emailBean.cleanData()}"
                                    onclick="PF('dialogAnadirEmail').hide();" 
                                />
                            </p:dialog>

                            <!-- BOTONES  -->
                            <h:panelGroup id="botonera">
                                <div class="botones grandes izquierda">

                                    <p:commandButton value="#{lbl.bt_anadir}"
                                        styleClass="botonOtros"
                                        action="#{emailBean.editarEmailCrear()}" immediate="true"
                                        oncomplete="PF('dialogAnadirEmail').show();" process="@this"
                                        update=":formApertura:acordeon:dialogAnadirEmail" />

                                </div>
                            </h:panelGroup>
                            <!--   -->
                        </p:tab>
                    </p:accordionPanel>

                    <p:confirmDialog global="true" showEffect="fade" hideEffect="fade">
                        <p:commandButton value="Sí" styleClass="ui-confirmdialog-yes"
                            icon="ui-icon-check" />
                        <p:commandButton value="No" styleClass="ui-confirmdialog-no"
                            icon="ui-icon-close" />
                    </p:confirmDialog>

                </h:form>
            </div>
        </div>
    </ui:define>
    <ui:define name="footpage">
        <ui:include src="/facelets/foot.xhtml"></ui:include>
    </ui:define>
</ui:composition>

My Bean class:

package es.ja.csalud.sas.intranet.buzoncontacto.configuracion.presentation.boundary;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.enterprise.context.SessionScoped;

import javax.inject.Inject;
import javax.inject.Named;

import org.joda.time.DateTime;
import org.primefaces.PrimeFaces;
import org.primefaces.event.SelectEvent;

import es.ja.csalud.sas.intranet.buzoncontacto.centros.business.entity.Centro;
import es.ja.csalud.sas.intranet.buzoncontacto.configuracion.business.boundary.Emails;
import es.ja.csalud.sas.intranet.buzoncontacto.configuracion.business.entity.Email;
import es.ja.csalud.sas.intranet.buzoncontacto.constantes.Cadenas;
import es.ja.csalud.sas.intranet.buzoncontacto.constantes.Presentacion;
import es.ja.csalud.sas.intranet.buzoncontacto.gestoresInternos.business.boundary.GestoresInternos;
import es.ja.csalud.sas.intranet.buzoncontacto.grupo.business.boundary.Grupos;
import es.ja.csalud.sas.intranet.buzoncontacto.grupo.business.entity.Grupo;
import es.ja.csalud.sas.intranet.buzoncontacto.presentation.BaseBean;
import es.ja.csalud.sas.intranet.buzoncontacto.presentation.helper.Util;
import es.ja.csalud.sas.intranet.buzoncontacto.springsecurity.presentation.boundary.UserReclamacionesBean;

@Named
@SessionScoped
public class EmailBean extends BaseBean implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = -2075577773794936417L;

    @Inject
    private Emails emails;

    @Inject
    protected transient UserReclamacionesBean userReclamacionesBean;

    @Inject
    protected Grupos grupos;

    @Inject
    private GestoresInternos gestoresInternos;

    private List<Email> listaEmails;

    private Email emailSeleccionada;

    @Inject
    private UserReclamacionesBean user;

    private Email emailEditar; 
    
    private Centro centroUsuario = null;

    /**
     * Inits the.
     */
    @PostConstruct
    public void init() {
        // Obtener la lista de Emails que pertenecen al Centro Hopitalario del Usuario
        this.listaEmails = new ArrayList<>();
        this.cargaListaEmail();

        this.emailSeleccionada = new Email();
        this.emailEditar = new Email();
    }

    /**
     * Actualiza a traves de la sesion el Centro Hospitalario al que pertenece (o ha
     * seleccionado) el Usuario.
     */
    public void actualizaCentroSeleccionadoUsuario() {
        Object objCentro = getSession().getAttribute(Cadenas.CENTRO_SELECCIONADO_USUARIO);
        if (objCentro instanceof Centro) {
            centroUsuario = (Centro) objCentro;
        }
    }

    public void cargaListaEmail() {
        this.actualizaCentroSeleccionadoUsuario();

        if (null != this.listaEmails) {
            if (this.userReclamacionesBean.getRole().equals(Cadenas.BCS_ADMINISTRADOR)) {
                this.listaEmails = this.emails.findByCentro(centroUsuario.getId());
            }else if(this.userReclamacionesBean.getRole().equals(Cadenas.BCS_GESTOR_INTERNO)) {
                Grupo grupo = grupoEmail();

                this.listaEmails = this.emails.findByCentroYGrupo(grupo, centroUsuario.getId());
            }
            
        }
    }

    private Grupo grupoEmail() {
        Grupo grupo;
        if (this.userReclamacionesBean.getRole().equals(Cadenas.BCS_ADMINISTRADOR)) {
            grupo = this.grupos.getById(0L);
        } else {
            grupo = this.gestoresInternos.getGestorInternoByDNI(userReclamacionesBean.getIdentificador())
                    .getGrupoGestorInterno();
        }
        return grupo;
    }

   public void editarEmail() {
        this.emailEditar = new Email(this.emailSeleccionada);
        System.out.println("editarEmail = " + this.emailEditar.getNombreEmail());
    }

   public void prepararEdicion(Email email) {
        this.emailSeleccionada = email;
        System.out.println("email = " + email.getNombreEmail());
        editarEmail();
    }

    public void saveEmail() {
        boolean insert = true;
        Grupo grupo = this.grupoEmail();

        // Validacion
        if (this.validateEmail()) {

            if (null == this.emailEditar.getId()) {
                this.emailEditar.setFechaInicioVigencia(new DateTime());
                this.emailEditar.setCreador(user.getNameUser());
                this.emailEditar.setFechaCreacion(new DateTime());
                this.emailEditar.setFechaModificacion(new DateTime());
                this.emailEditar.setModificador(user.getNameUser());
                this.emailEditar.setGrupo(grupo);
                this.emailEditar.setIdCentro(centroUsuario.getId());
                
            } else {

                this.emailEditar.setFechaInicioVigencia(new DateTime());
                this.emailEditar.setFechaModificacion(new DateTime());
                this.emailEditar.setModificador(user.getNameUser());
                this.emailEditar.setGrupo(grupo);
                insert = false;
            }

            this.emails.guardar(this.emailEditar);

            this.emails.flush();

            if (insert) {
                this.addMessageInfo(Presentacion.LBL.getString("Guardado_correcto"),
                        Presentacion.LBL.getString("EMAIL_CONFIGURACION_CREADA"));
            }

            if (!insert) {
                this.addMessageInfo(Presentacion.LBL.getString("Guardado_correcto"),
                        Presentacion.LBL.getString("EMAIL_CONFIGURACION_MODIFICADA"));

            }

            PrimeFaces.current().executeScript("PF('dialogAnadirEmail').hide()");
            this.emailEditar = new Email();
            this.cargaListaEmail();
        }
    }

    public void cleanData() {
        this.emailEditar = new Email();
        //this.emailSeleccionada = new Email();
        //PrimeFaces.current().ajax().update("formApertura:acordeon:dialogAnadirEmail");

    }

    public void borrarEmail() {
        emailSeleccionada.setFechaFinVigencia(new DateTime());
        emailSeleccionada.setActivo(false);
        this.emails.update(emailSeleccionada);
        this.emails.flush();
        this.emailEditar = new Email();
        this.emailSeleccionada = new Email();
        this.addMessageInfo(Presentacion.EXITO_ELIMINAR, "");
        this.cargaListaEmail();
    }

    protected boolean validateEmail() {
        boolean validacion = true;

        // Validamos que sea un email correcto
        if (!Util.validateEmailNoCorporativo(this.emailEditar.getNombreEmail())) {
            this.addMessageError(Presentacion.ERROR_VALID, Cadenas.ERR_CORREO_INVALIDO);
            validacion = false;
        }

        return validacion;
    }

    public void onRowSelect(SelectEvent event) {
        final Email p = (Email) event.getObject();
        this.emailSeleccionada = p;
    }

    public void editarEmailCrear() {
        this.emailEditar = new Email();
    }

    public List<Email> getListaEmails() {
        return listaEmails;
    }

    public void setListaEmails(List<Email> listaEmail) {
        this.listaEmails = listaEmail;
    }

    public Email getEmailSeleccionada() {
        return emailSeleccionada;
    }

    public void setEmailSeleccionada(Email tipoMasivaSeleccionada) {
        this.emailSeleccionada = tipoMasivaSeleccionada;
    }

    public Email getEmailEditar() {
        return emailEditar;
    }

    public void setEmailEditar(Email tipoMasivaEditar) {
        this.emailEditar = tipoMasivaEditar;
    }

    public Centro getCentroUsuario() {
        return centroUsuario;
    }

    public void setCentroUsuario(Centro centroUsuario) {
        this.centroUsuario = centroUsuario;
    }

}

Solution

  • The culprit is here:

    <f:event type="preRenderView" listener="#{emailBean.cargaListaEmail}" />
    

    This basically reloads the model behind the <p:dataTable> during every HTTP request. Note that an Ajax request also counts as a HTTP request. When that Ajax request doesn't include an update of the <p:dataTable>, then the presentation won't necessarily reflect the actual contents of the reloaded model. By default the records are selected by list index, so when the reloaded model has a different item in the selected index than being presented to the enduser, then the enduser will have the experience of seeing a "random" item.

    Just remove that preRenderView listener. You don't need it here. You're already explicitly loading the model in the @PostConstruct and in the save method of your backing bean. That's more than sufficient.

    In case you really need to reload the model during every request for some reason, then you can alternatively also instruct the <p:dataTable> to select the records by entity ID instead of list index. You can do so by specifying the rowKey attribute.

    <p:dataTable ...
                 value="#{emailBean.listaEmails}" 
                 var="email" 
                 rowKey="#{email.id}"
                 ...>
    

    See also:


    That said, PrimeFaces 5.x is ancient (released May 2014, thus currently almost 10 years old!) and has serious security bugs. Try to keep your software up to date. See also among others JSF Cryptojacking Malware.