1

I'm using JSF 2.2 in a web application and I'm having problems in the view when I use f:validateRegex and fails (because when I use immediate="true" and try to navigate to the same page again, the view isn't updated when I have a new Instance of the object in my backing bean). I was thinking richfaces has a bug (because I'm using jsf and richfaces in my main application) so I made a test code with richfaces and without richfaces (only jsf) to identify where is the error, but in both cases the view fails.

Here is my test code without richfaces (Only jsf):

View:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core"
      xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">
    <h:head>
        <title>Mis pruebas con JSF</title>
    </h:head>
    <h:body>
        <h:form id="lista">
            <h:panelGrid id="principal">
                <h:dataTable value="#{indexBB.personas}" var="persona">
                    <h:column>
                        <f:facet name="header">Activo</f:facet>
                        <h:selectBooleanCheckbox value="#{persona.activo}"></h:selectBooleanCheckbox>
                    </h:column>
                    <h:column>
                        <f:facet name="header">Nombre</f:facet>
                        <h:outputText value="#{persona.nombre}"></h:outputText>
                    </h:column>
                    <h:column>
                        <f:facet name="header">Correo</f:facet>
                        <h:outputText value="#{persona.correo}"></h:outputText>
                    </h:column>
                </h:dataTable>
                <h:commandButton action="#{indexBB.crearPersona}" value="Crear Persona">
                </h:commandButton>
                <h:commandButton action="#{indexBB.activarBoton}" value="Activar Boton">
                </h:commandButton>
            </h:panelGrid>
        </h:form>
        <h:form id="crear">
            <h:panelGrid id="secundario" rendered="#{indexBB.crear}">
                <h:outputText value="Activo?">
                </h:outputText>
                <h:selectBooleanCheckbox label="Activo" value="#{indexBB.persona.activo}">
                </h:selectBooleanCheckbox>
                <br></br>
                <h:outputText value="Nombre"></h:outputText>
                <h:inputText label="Nombre" value="#{indexBB.persona.nombre}">
                </h:inputText>
                <br></br>
                <h:outputText value="Correo"></h:outputText>
                <h:inputText label="Nombre" value="#{indexBB.persona.correo}">
                    <f:validateRegex
                        pattern="[\w\.-]*[a-zA-Z0-9_]@[\w\.-]*[a-zA-Z0-9]\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]" />
                </h:inputText>
                <br></br>
                <h:commandButton action="#{indexBB.guardarPersona}" value="Guardar Persona">
                </h:commandButton>
                <h:commandButton action="#{indexBB.cancelar}" value="Cancelar" immediate="true">
                </h:commandButton>
            </h:panelGrid>
        </h:form>
    </h:body>
</html>

Bean:

package com.kanayet.martin.view.bb;

import com.kanayet.martin.model.entity.Persona;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.inject.Named;
import javax.faces.view.ViewScoped;

@Named(value = "indexBB")
@ViewScoped
public class indexBB implements Serializable {

    private Persona persona;
    private List<Persona> personas;
    private boolean crear;

    /**
     * Creates a new instance of indexBB
     */
    public indexBB() {
    }

    @PostConstruct
    public void onInit(){
        personas = new ArrayList<>();
        personas.add(new Persona("Martin", "martin@gmail.com", true));
        personas.add(new Persona("Andrea", "andrea@gmail.com", true));
        personas.add(new Persona("Camilo", "camilo@gmail.com", true));
        personas.add(new Persona("Felipe", "felipe@gmail.com", true));
        personas.add(new Persona("David", "david@gmail.com", true));
    }

    public void activarBoton() {
        persona = personas.get(0);
    }

    public void crearPersona(){
        crear = true;
        persona = new Persona();
    }

    public void guardarPersona(){
        personas.set(0, persona);
    }

    public void cancelar(){
    }

    public Persona getPersona() {
        return persona;
    }

    public void setPersona(Persona persona) {
        this.persona = persona;
    }

    public List<Persona> getPersonas() {
        return personas;
    }

    public void setPersonas(List<Persona> personas) {
        this.personas = personas;
    }

    public boolean isCrear() {
        return crear;
    }

    public void setCrear(boolean crear) {
        this.crear = crear;
    }

}

Model: (Object)

package com.kanayet.martin.model.entity;   

public class Persona {

    private String nombre;
    private String correo;
    private Boolean activo;

    public Persona() {
    }

    public Persona(String nombre, String correo, Boolean activo) {
        this.nombre = nombre;
        this.correo = correo;
        this.activo = activo;
    }

    public String getNombre() {
        return nombre;
    }

    public void setNombre(String nombre) {
        this.nombre = nombre;
    }

    public String getCorreo() {
        return correo;
    }

    public void setCorreo(String correo) {
        this.correo = correo;
    }

    public Boolean getActivo() {
        return activo;
    }

    public void setActivo(Boolean activo) {
        this.activo = activo;
    }

}

Here is my test code with richfaces: (Bean and Model are the same)

View:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:a4j="http://richfaces.org/a4j"
      xmlns:rich="http://richfaces.org/rich"
      xmlns:f="http://xmlns.jcp.org/jsf/core">
    <h:head>
        <title>Mis pruebas con RichFaces</title>
    </h:head>
    <h:body>
        <h:form id="lista">
            <a4j:outputPanel id="principal">
                <rich:dataTable id="personas" value="#{indexBB.personas}"
                                var="persona" rows="50">
                    <rich:column>
                        <h:selectBooleanCheckbox label="Activo" value="#{persona.activo}">
                        </h:selectBooleanCheckbox>
                    </rich:column>
                    <rich:column>
                        <h:outputText value="#{persona.nombre}"></h:outputText>
                    </rich:column>
                    <rich:column>
                        <h:outputText value="#{persona.correo}"></h:outputText>
                    </rich:column>
                </rich:dataTable>
                <h:commandButton action="#{indexBB.crearPersona}" value="Crear Persona">
                </h:commandButton>
                <h:commandButton action="#{indexBB.activarBoton}" value="Activar Boton">
                </h:commandButton>
            </a4j:outputPanel>
        </h:form>
        <br></br>
        <h:form id="crear">
            <a4j:outputPanel id="secundario" rendered="#{indexBB.crear}">
                <h:outputText value="Activo?">
                </h:outputText>
                <h:selectBooleanCheckbox label="Activo" value="#{indexBB.persona.activo}">
                </h:selectBooleanCheckbox>
                <br></br>
                <h:outputText value="Nombre"></h:outputText>
                <h:inputText label="Nombre" value="#{indexBB.persona.nombre}">
                </h:inputText>
                <br></br>
                <h:outputText value="Correo"></h:outputText>
                <h:inputText label="Nombre" value="#{indexBB.persona.correo}">
                    <f:validateRegex
                        pattern="[\w\.-]*[a-zA-Z0-9_]@[\w\.-]*[a-zA-Z0-9]\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]" />
                </h:inputText>
                <br></br>
                <h:commandButton action="#{indexBB.guardarPersona}" value="Guardar Persona">
                </h:commandButton>
                <h:commandButton action="#{indexBB.cancelar}" value="Cancelar" immediate="true">
                </h:commandButton>
            </a4j:outputPanel>
        </h:form>
    </h:body>
</html>

The problem is when I click "Crear Persona" button, I write for example "Nombre": Felix and "Correo": Felix and click "Guardar Persona" button so f:validateRegex fails because isn't a valid email, then click "Cancelar" because my final user doesn't know email required value (immediate="true"). Again, click "Crear Persona" button, (new object in my bean) and jsf page isn't updated, the form should be empty but it isn't, in field "Nombre" stills "Felix" value, but in my bean I have a new and empty object without values in its attributes, do you know why?

The problem is with and without richfaces (because I thought the problem could be richfaces, but it isn't), so I don't know why jsf page isn't updated if I have a new object in my bean, I used netbeans debug tool to verify but I'm right, the object that I see in my bean is different (server side new and empty object) but in my JSF page "Nombre" has "Felix" value and I want to know why it happens, and how I can resolve this problem.

Thank you so much.

1 Answers1

1

The problem is that JSF maintains two representations of your model. There is the Java object, IndexBB, but there is also the component tree, the thing that keeps track of UI state.

When you fail validation, the component tree still contains the values entered. (This is a useful feature so that the user can correct the values.) You've used immediate=true to skip validation, but that doesn't reset the component tree values.

In JSF 2.2, you can use resetValues to reset component tree values:

<h:form id="crear">
    <h:panelGrid id="secundario" rendered="#{indexBB.crear}">
        <h:outputText value="Activo?">
        </h:outputText>
        <h:selectBooleanCheckbox label="Activo" value="#{indexBB.persona.activo}">
        </h:selectBooleanCheckbox>
        <br></br>
        <h:outputText value="Nombre"></h:outputText>
        <h:inputText id="nombreId" label="Nombre" value="#{indexBB.persona.nombre}">
        </h:inputText>
        <br></br>
        <h:outputText value="Correo"></h:outputText>
        <h:inputText id="correoId" label="Nombre" value="#{indexBB.persona.correo}">
            <f:validateRegex
                pattern="[\w\.-]*[a-zA-Z0-9_]@[\w\.-]*[a-zA-Z0-9]\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]" />
        </h:inputText>
        <br></br>
        <h:commandButton  action="#{indexBB.guardarPersona}" value="Guardar Persona">
        </h:commandButton>
        <h:commandButton 
            action="#{indexBB.cancelar}" value="Cancelar">
            <f:ajax resetValues="true" render="crear:nombreId crear:correoId"/>
        </h:commandButton>
    </h:panelGrid>
</h:form>

Changes:

  1. Remove immediate=true.
  2. Add ids to inputs you want to reset.
  3. Add f:ajax to Cancelar button.
  4. Add resetValues property to f:ajax and list your IDs (separate IDs with spaces, not comma).

Make sure your cancelar method actually resets persona -- the code you posted doesn't do this.

If you also want to reset the error messages, add an h:messages to the form, give it an ID, and reset it too.

See also

Community
  • 1
  • 1
DavidS
  • 5,022
  • 2
  • 28
  • 55