2010-10-14 3 views
11


Je ne comprends pas le comportement de JSF2 pendant la valdation. J'espère que quelqu'un peut m'aider.JSF 2 - Validation du bean: échec de la validation -> les valeurs vides sont remplacées par les dernières valeurs valides du bean géré

J'ai une forme où les champs sont validés après (ajax) soumettre - ok
Si la validation a échoué un message d'erreur est affiché - ok

Pour mon exemple quand j'entre un valide anniversaire et champ nom est vide un message d'erreur pour nom est affiché après soumettre.
Maintenant, quand j'entrer un nom valide et supprimer l'entrée du champ d'anniversaire un errormessage est spectacle pour anniversaire (c'est ok) mais maintenant l'ancien « valide » anniversaire se distingue également dans le champ de saisie! ?!

Comment puis-je éviter ce comportement? Quand je soumets un champ vide Je veux voir un errormessage et un champ vide ...

Voici mon exemple de code:

J'utilise un ManagedBean (TestBean) qui contient un EntityBean (Contacter). Le Contact contient des validations par annotation.

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

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

    //... 
} 

Mon 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;} 
} 

Une ici ma page JSF:

<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> 

enfin un extrait de 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> 

Merci pour quelques conseils

Répondre

8

Votre problème particulier est causé par

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

et un bug (au moins, un oubli) au HtmlBasicRenderer#getCurrentValue() de 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; 

Normalement, la valeur soumise est définie sur null lorsque le composant UIInput a été converti et validé avec succès. Lorsque JSF est sur le point de réafficher la valeur, il vérifie d'abord si la valeur soumise n'est pas null avant de réafficher la valeur du modèle. Toutefois, avec ce paramètre de contexte, il est null au lieu d'une chaîne vide lorsqu'il est invalide et donc il affichera toujours la valeur du modèle d'origine lorsque vous supprimez la valeur initiale d'un champ obligatoire.

Pour le tester, définissez cette valeur de paramètre de contexte sur false ou supprimez-la complètement. Vous verrez que cela fonctionne comme prévu. Cependant, l'inconvénient sera que les valeurs de votre modèle seront encombrées avec des chaînes vides sur les champs vides mais non obligatoires et vous perdrez l'avantage d'utiliser l'annotation @NotNull de la validation du bean JSR 303.

Pour résoudre ce problème, vous avez à modifier la première partie de HtmlBasicRenderer#getCurrentValue() comme suit:

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; 
    } 
} 

je l'ai déjà signalé à des gars comme Mojarra issue 2262.

+0

J'ai remarqué un comportement similaire lorsqu'une entrée est liée à une propriété non-String avec un convertisseur, probablement en raison de la même cause. Lorsque vous insérez une chaîne vide et que la validation échoue en raison d'un champ différent, votre chaîne vide (valide) est remplacée par la valeur du bean. A été signalé comme http://java.net/jira/browse/JAVASERVERFACES-838 – wrschneider

+2

@BalusC: Merci pour votre explication détaillée! Vos solutions/explications ici font souvent ma journée ;-) –

+0

@BalusC Cela signifie-t-il qu'il compile une version personnalisée de JSF? J'ai rencontré ce même problème et j'espère que ce n'est pas la seule solution. – MikeR

0

Je pense qu'en utilisation normale, les utilisateurs ne saisiront pas de date valide, ne la soumettront pas et supprimeront la date avant de la soumettre à nouveau. Je me rends compte que vous avez trouvé cela pendant les tests, mais les gens essayent probablement de remplir le formulaire avec succès et de ne pas supprimer les éléments qu'ils ont déjà saisis, auquel cas conserver la dernière valeur valide est la meilleure fonctionnalité. Si vous insistez ... Il semble que la méthode de réglage "anniversaire" ne soit jamais appelée parce que la valeur n'est pas valide, puis lorsque la page est réaffichée, la valeur actuelle de "anniversaire" est affichée (la valeur actuelle étant le valeur valide précédemment enregistrée). Peut-être que vous pourriez écrire un validateur personnalisé qui définit la valeur et ALORS valide la valeur, mais cela n'aurait pas vraiment de sens. Vous devrez toujours valider la valeur en premier pour les cas où les utilisateurs saisissent une chaîne de texte comme "hier" au lieu d'une date valide, puis vous devez définir la date sur quelque chose basé sur cette valeur non valide, et vous auriez alors pour ajouter le message au FacesContext. Donc, le code devrait faire quelque chose comme ce qui suit.

1) valider la valeur de la date
2) si elle n'est pas valide, définissez le champ à une valeur logique et ajoutez un message d'erreur à FacesContext.
3) si c'est valide, alors utilisez-le.

C'est faisables, mais bizarre parce que vous changez la valeur du champ même si la valeur passée est invalide ...

+0

Salut Aaron, merci pour votre réponse! J'ai mon exemple de code il y a un autre cas qui montre le même problème en essayant de remplacer le modèle backend (bouton newContact). Pour moi, cela ressemble à un problème du monde réel - et pas à un cas d'école. J'ai trouvé une solution (pas sympa, mais ça marche ...).Pour les solutions j'ai édité l'exemple de code un peu. Voir ma propre réponse –

0

Aaron descripes déjà le comportement.

Le problème que l'on vient de décrire existe aussi en cliquant sur le bouton 'newContact'. Si la première soumission n'est pas valide (anniversaire a été entré, champ de nom est vide) un message d'erreur est affiché. D'accord. Ensuite, le bouton "newContact" n'actualise pas (n'efface pas) la vue. Bien que le modèle ait été réinstallé (contact = new Contact()).

J'ai trouvé quelques Tipps ici: http://wiki.apache.org/myfaces/ClearInputComponents

Voici ma solution:

public void newContact(ActionEvent ae) { 
    contact = new Contact(); 
    contact.setBirthday(new Date()); //for testing only 

    resetForm(ae.getComponent()); 
} 

private void resetForm(UIComponent uiComponent) { 
    //get form component 
    UIComponent parentComponent = uiComponent.getParent(); 
    if (uiComponent instanceof UIForm) 
     resetFields(uiComponent); 
    else if (parentComponent != null) 
     resetForm(parentComponent); 
    else 
     resetFields(uiComponent); 

} 

private void resetFields(UIComponent baseComponent) { 
    for (UIComponent c : baseComponent.getChildren()) { 
     if (c.getChildCount() > 0) 
      resetFields(c); 

     if (c instanceof UIInput) 
      ((UIInput) c).resetValue(); 
    } 
} 
0

Problème similaire: une valeur chargée à partir du bean backing était réinitialisée lorsque le champ était masqué et qu'un autre composant échouait à la validation. J'ai dû faire un léger ajout au code de BalusC pour le faire fonctionner.

protected String getCurrentValue(FacesContext context, 
            UIComponent component) { 

     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; 
      } 
     } 


     String currentValue = null; 
     Object currentObj; 

     if (component instanceof UIInput && ((UIInput)component).isLocalValueSet()) 
     { 
      currentObj = ((UIInput)component).getLocalValue(); 
     } 
     else { 
      currentObj = getValue(component); 
     } 

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


    } 
Questions connexes