Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JSF 2 - Bean Validation: validation failed -> empty values are replaced with last valid values from managed bean


I do not understand the behaviour of JSF2 during valdation. Hope someone can help me.

I have a form where the fields are validated after (ajax) submit - ok
If the validation failed a error message is shown - ok

For my example when I enter a valid birthday and the field name is empty an errormessage for name is shown after submit.
Now when I enter a valid name and delete the input from the birthday field an errormessage is show for birthday (that's ok) but now the old 'valid' birthday stands also in the input field!?!

How can I avoid this behaviour? When I submit an empty field I want to see an errormessage and an empty field...

Here's my sample code:

I use a ManagedBean (TestBean) that contains an EntityBean (Contact). The Contact contains validations per annoations.

public class Contact implements Serializable {
    @NotNull 
    @Temporal(TemporalType.DATE)
    private Date birthday;

    @NotNull 
    @Size(min=3, max=15)
    private String name;

    //...
}

My ManagedBean:

@ManagedBean
@ViewScoped
public class TestBean implements Serializable {
    private Contact contact;

    @PostConstruct
    void init() {
        System.out.println("init...");
        contact = new Contact(); 
    }

    public void newContact(ActionEvent ae) {
        System.out.println("newContact...");
        contact = new Contact();
    }

    public void save() {
        System.out.println("save...");
        //TODO do something with contact...
    }

    public Contact getContact() { return contact; }

    public void setContact(Contact contact) {this.contact = contact;}
}

An here my JSF page:

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core" >      
 <h:body>
    <h:form>     
        <h:panelGrid columns="3">   

        <h:outputText value="Birthday: " />  
        <h:inputText id="birthday" value="#{testBean.contact.birthday}">
            <f:convertDateTime/>
        </h:inputText>  
        <h:message for="birthday" />  

        <h:outputText value="Name: " />  
        <h:inputText id="name" value="#{testBean.contact.name}"/>
        <h:message for="name" />

        </h:panelGrid>  

        <h:commandButton value="submit" action="#{testBean.save}"> 
            <f:ajax execute="@form" render="@form"/>
        </h:commandButton> 

        <h:commandButton value="newContact" actionListener="#{testBean.newContact}"
                         immediate="true"> 
            <f:ajax render="@form"/>
        </h:commandButton> 

    </h:form>
</h:body>
</html>

at last a snippet from web.xml

<context-param>
    <param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name>
    <param-value>true</param-value>
</context-param>

<context-param>
    <param-name>javax.faces.VALIDATE_EMPTY_FIELDS</param-name>
    <param-value>true</param-value>
</context-param>

Thanks for some tips

like image 264
Matthias Reining Avatar asked Oct 14 '10 13:10

Matthias Reining


1 Answers

Your particular problem is caused by

<context-param>
    <param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name>
    <param-value>true</param-value>
</context-param>

and a bug (at least, an oversight) in HtmlBasicRenderer#getCurrentValue() of Mojarra:

if (component instanceof UIInput) {
    Object submittedValue = ((UIInput) component).getSubmittedValue();
    if (submittedValue != null) {
        // value may not be a String...
        return submittedValue.toString();
    }
}

String currentValue = null;
Object currentObj = getValue(component);
if (currentObj != null) {
    currentValue = getFormattedValue(context, component, currentObj);
}
return currentValue;

Normally, the submitted value is set to null when the UIInput component is successfully converted and validated. When JSF is about to redisplay the value, it first checks if the submitted value is not null before proceeding to redisplay the model value. However, with this context parameter, it is null instead of an empty string when it is invalid and thus it will always redisplay the original model value when you remove the initial value of a required field.

To test it, set that context param value to false or remove it altogether. You'll see that it works as intended. However, it will bring back the disadvantage that your model values will be cluttered with empty strings on empty but non-required fields and you'll lose the advantage of using @NotNull annotation of JSR 303 bean validation.

To fix this, you've to alter the first part of HtmlBasicRenderer#getCurrentValue() as follows:

if (component instanceof UIInput && !((UIInput) component).isValid()) {
    Object submittedValue = ((UIInput) component).getSubmittedValue();
    if (submittedValue != null) {
        // value may not be a String...
        return submittedValue.toString();
    } else {
        return null;
    }
}

I've already reported it to Mojarra guys as issue 2266.

like image 196
BalusC Avatar answered Oct 15 '22 10:10

BalusC