2010-10-14 9 views
11


Nie rozumiem zachowania JSF2 podczas valdation. Mam nadzieję, że ktoś może mi pomóc.JSF 2 - Bean Validation: sprawdzanie poprawności nie powiodło się -> puste wartości są zastępowane ostatnimi prawidłowymi wartościami z zarządzanego komponentu bean

Mam formularza gdzie pola są sprawdzane po (Ajax) złożyć - ok
Jeśli sprawdzanie poprawności powiodło się komunikat o błędzie jest wyświetlany - ok

Na moim przykładzie, kiedy podać poprawny urodziny i pole nazwa jest pusta errormessage dla nazwa jest wyświetlana po przesłaniu.
Teraz, kiedy podać poprawny nazwę i usuń wejście z dziedzinie urodziny ErrorMessage jest pokazać urodzin (to jest ok), ale teraz stara „ważny” urodziny także stoi w polu tekstowym! ?!

Jak mogę uniknąć tego zachowania? Kiedy złożyć puste pole Chcę zobaczyć ErrorMessage i pustego pola ...

Oto mój przykładowy kod:

używam ManagedBean (TestBean), który zawiera EntityBean (nas). Kontakt zawiera zatwierdzenia na anonimizacji.

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

tutaj moja strona 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> 

na ostatni fragment z 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> 

Dziękujemy za porady

Odpowiedz

8

Twój szczególny problem jest spowodowany przez

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

i błędu (przynajmniej, przeoczenie) w HtmlBasicRenderer#getCurrentValue() z 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; 

Zwykle przesłana wartość jest ustawiona na null, gdy komponent UIInput zostanie pomyślnie przekonwertowany i sprawdzony. Kiedy JSF ma zamiar ponownie wyświetlić wartość, najpierw sprawdza, czy przesłana wartość nie jest równa null, zanim przejdzie do ponownego wyświetlenia wartości modelu. Jednak w przypadku tego parametru kontekstowego zamiast pustego łańcucha, gdy jest on niepoprawny, jest on pusty i dlatego zawsze będzie wyświetlał pierwotną wartość modelu po usunięciu początkowej wartości wymaganego pola.

Aby to przetestować, ustaw tę wartość parametru kontekstu na false lub całkowicie ją usuń. Zobaczysz, że działa zgodnie z przeznaczeniem. Jednak spowoduje to wadę polegającą na tym, że twoje wartości w modelu zostaną zajęte pustymi ciągami na pustych, ale niewymaganych polach, a utracisz przewagę dzięki stosowaniu sprawdzania fasoli JSR 303 w JSR 303.

Aby rozwiązać ten problem, masz do zmiany pierwszą część HtmlBasicRenderer#getCurrentValue() następująco:

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

Ja już zgłosiłem to do Mojarra facetów jak issue 2262.

+0

Zauważyłem podobne zachowanie, gdy dane wejściowe są powiązane z właściwością inną niż String z konwerterem, prawdopodobnie z tej samej przyczyny źródłowej. Gdy wstawisz pusty łańcuch, a sprawdzanie poprawności nie powiedzie się z powodu innego pola, Twój (prawidłowy) pusty łańcuch zostanie zastąpiony wartością z komponentu bean. Został zgłoszony jako http://java.net/jira/browse/JAVASERVERFACES-838 – wrschneider

+2

@BalusC: Dziękujemy za szczegółowe wyjaśnienie! Twoje rozwiązania/wyjaśnienia tutaj często sprawiają, że mój dzień ;-) –

+0

@BalusC Czy to oznacza, że ​​kompiluje niestandardową wersję JSF? Napotkałem ten sam problem i mam nadzieję, że nie jest to jedyne rozwiązanie. – MikeR

0

Myślę, że podczas normalnego użytkowania ludzie nie wprowadzą poprawnej daty, nie złożą, a następnie nie usuną daty przed ponownym wysłaniem. Zdaję sobie sprawę, że znalazłeś to podczas testów, ale prawdopodobnie ludzie starają się tylko wypełnić formularz i nie usuwać rzeczy, które już wprowadzili. W takim przypadku zachowanie ostatniej ważnej wartości jest najlepszą funkcjonalnością.

Jeśli nalegasz ... Wygląda na to, że metoda settera "urodziny" nigdy nie jest wywoływana, ponieważ wartość jest nieprawidłowa, a następnie, gdy strona jest wyświetlana ponownie, wyświetlana jest aktualna wartość "urodziny" (aktualna wartość to poprawna wartość, która została wcześniej zapisana). Może mógłbyś napisać niestandardowy weryfikator, który ustawi wartość, a THEN sprawdza poprawność wartości, ale nie miałoby to większego sensu. Nadal musisz najpierw sprawdzić poprawność wartości dla przypadków, w których użytkownicy wprowadzają ciąg tekstowy, taki jak "wczoraj" zamiast daty prawidłowej, a następnie musisz ustawić datę na podstawie tej nieprawidłowej wartości, a następnie , aby dodać wiadomość do FacesContext. Tak więc kod musiałby zrobić coś takiego jak poniżej.

1) sprawdź poprawność wartości daty
2) jeśli jest ona nieważna, ustaw pole na wartość, która ma sens i dodaj komunikat o błędzie do FacesContext.
3) jeśli jest ważny, użyj go.

to zrobić-stanie, ale dziwne, bo jesteś zmieniając wartość pola, mimo że przeszedł w wartości jest nieważny ...

+0

Cześć Aaron, dziękuję za odpowiedź! I mój przykładowy kod jest inny przypadek, który pokazuje ten sam problem podczas próby zastąpienia modelu zaplecza (przycisk newContact). Dla mnie wygląda to na problem z prawdziwego świata - a nie jak akademicki przypadek testowy. Znalazłem rozwiązanie (nie ładne, ale działa ...).Dla rozwiązań trochę edytowałem przykładowy kod. Zobacz moją własną odpowiedź –

0

Aaron już descripes zachowanie.

Problem, który został opisany, istnieje również poprzez kliknięcie przycisku "nowyKontakt". Jeśli pierwszy przekaz jest niepoprawny (wprowadzono datę urodzenia, pole nazwy jest puste) wyświetlany jest komunikat o błędzie. ok.

Następnie przycisk "newContact" nie odświeża widoku. Chociaż model został zresetowany (contact = new Contact()).

znalazłem kilka Tipps tutaj: http://wiki.apache.org/myfaces/ClearInputComponents

Oto moje rozwiązanie:

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

Wystąpił podobny problem, gdy wartość załadowana z komponentu bean backget zostałaby zresetowana, gdy pole było wygaszone, a inny składnik nie sprawdził poprawności. Musiałem wprowadzić niewielki dodatek do kodu BalusC, żeby to zadziałało.

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; 


    } 
Powiązane problemy