2010-10-14 14 views
11


Non capisco il comportamento di JSF2 durante la valdation. Spero che qualcuno possa aiutarmi.JSF 2 - Convalida del bean: convalida non riuscita -> i valori vuoti vengono sostituiti con gli ultimi valori validi dal bean gestito

Ho una forma in cui i campi sono convalidati dopo (Ajax) presentare - ok
Se la convalida non è riuscita viene visualizzato un messaggio di errore - ok

Per il mio esempio, quando entro in una valida compleanno e la il campo nome è vuoto un messaggio errato per nome viene visualizzato dopo l'invio.
Ora, quando entro in un nome valido ed eliminare l'ingresso dal compleanno campo un messaggio d'errore è spettacolo per compleanno (che è ok), ma ora il vecchio 'valida' compleanno si distingue anche nel campo di input! ?!

Come posso evitare questo comportamento? Quando presento un campo vuoto Voglio vedere un messaggio d'errore e un campo vuoto ...

Ecco il mio codice di esempio:

io uso un ManagedBean (TestBean) che contiene un EntityBean (Contatto). Il contatto contiene convalide per i commenti.

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

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

    //... 
} 

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

Una qui la mia pagina 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> 

finalmente un frammento da 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> 

Grazie per alcuni suggerimenti

risposta

8

vostro particolare problema è causato da

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

e un bug (almeno, una svista) in HtmlBasicRenderer#getCurrentValue() di 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; 

In genere, il valore inviato viene impostato su null quando il componente UIInput viene convertito e convalidato correttamente. Quando JSF sta per rivisualizzare il valore, prima controlla se il valore inviato non è null prima di procedere alla rivisualizzazione del valore del modello. Tuttavia, con questo parametro di contesto, è null invece che una stringa vuota quando non è valida e pertanto restituirà sempre il valore del modello originale quando si rimuove il valore iniziale di un campo obbligatorio.

Per verificarlo, impostare il valore del parametro di contesto su false o rimuoverlo del tutto. Vedrai che funziona come previsto. Tuttavia, si tornerà lo svantaggio che i valori del modello saranno ingombri di stringhe vuote su campi vuoti ma non obbligatori e si perderà il vantaggio dell'uso dell'annotazione @NotNull per la convalida del bean JSR 303.

Per risolvere questo problema, hai a modificare la prima parte della HtmlBasicRenderer#getCurrentValue() come segue:

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

ho già segnalato a ragazzi Mojarra come issue 2262.

+0

ho notato un comportamento simile quando un ingresso è associato a un non-String con un convertitore, probabilmente dovuto alla stessa causa principale. Quando si inserisce una stringa vuota e la convalida non riesce a causa di un campo diverso, la stringa vuota (valida) viene sovrascritta con il valore dal bean. è stato segnalato come http://java.net/jira/browse/JAVASERVERFACES-838 – wrschneider

+2

@BalusC: Grazie per la spiegazione dettagliata! Le tue soluzioni/spiegazioni qui spesso rendono la mia giornata ;-) –

+0

@BalusC Significa che compila una versione personalizzata di JSF? Ho incontrato questo stesso problema e spero che questa non sia l'unica soluzione. – MikeR

0

Sto pensando che durante l'uso normale le persone non inseriranno una data valida, invia e quindi cancellano la data prima di inviarla di nuovo. Mi rendo conto che l'hai trovato durante i test, ma probabilmente le persone stanno solo cercando di compilare correttamente il modulo e non eliminare le cose che hanno già inserito, nel qual caso forse mantenere l'ultimo valore valido è la migliore funzionalità.

Se insisti ... Sembra che il metodo setter "compleanno" non viene mai chiamato perché il valore non è valido, e poi, quando la pagina viene visualizzata nuovamente il valore corrente di "compleanno" viene visualizzato (il valore corrente è il valore valido precedentemente salvato). Forse potresti scrivere un validatore personalizzato che imposta il valore e THEN convalida il valore, ma questo non avrebbe molto senso. Dovresti comunque convalidare il valore prima per i casi in cui gli utenti inseriscono una stringa di testo come "ieri" invece di una data valida e quindi devi impostare la data su qualcosa in base a quel valore non valido, e quindi avresti per aggiungere il messaggio a FacesContext. Quindi il codice dovrebbe fare qualcosa di simile al seguente.
1) convalidare il valore di data
2) se non è valido, impostare il campo su un valore che abbia senso e aggiungere un messaggio di errore a FacesContext.
3) se è valido, usalo.

Quel fattibile, ma strano perché stai cambiando il valore del campo, anche se il passato in valore non è valido ...

+0

Ciao Aaron, grazie per la tua risposta! Il mio codice di esempio è un altro caso che mostra lo stesso problema durante il tentativo di sostituire il modello di backend (pulsante newContact). Per me sembra un problema del mondo reale - e non come un caso di test accademico. Ho trovato una soluzione (non bella, ma funziona ...).Per le soluzioni ho modificato un po 'il codice di esempio. Vedere la mia risposta –

0

Aaron descripes già il comportamento.

Il problema che ho descritto esiste anche facendo clic sul pulsante "Nuovo contatto". Se il primo invio non è valido (è stato inserito il compleanno, il campo nome è vuoto) viene visualizzato un messaggio di errore. ok.

Successivamente il pulsante 'newContact' non aggiorna (cancella) la vista. Sebbene il modello sia stato resettato (contact = new Contact()).

ho trovato alcuni tipps qui: http://wiki.apache.org/myfaces/ClearInputComponents

Ecco la mia soluzione:

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

Si è verificato un problema simile in cui un valore caricato dal bean di supporto si ripristinava quando il campo veniva oscurato e un altro componente non riusciva a convalidare. Ho dovuto fare una piccola aggiunta al codice di BalusC per farlo funzionare.

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; 


    } 
Problemi correlati