poniedziałek, 26 stycznia 2009

Walidacja wielu pól w jednym walidatorze w JSF

Typowa sytuacja - mamy formularz zmiany hasła a w nim pola jak hasło i potwierdź hasło. Jak stworzyć do takich pół klasę walidującą skoro normalnie może ona operować na tylko jednym elemencie formularza ?
To proste:) Musimy przekazać dodatkowo walidatorowi jako atrybut wartość z pierwszego pola.
Posługując się powyższym przykładem mamy formularz:

<h:form id="changepassform">
<tr>
<td>Nowe hasło:</td>
<td>
<h:inputSecret id="password" value="#{userRegistrationBean.password}"
required="true" redisplay="false" />
</td>
</tr>
<tr>
<td>Powtórz:</td>
<td>
<h:inputSecret id="confirm" required="true" redisplay="false">
<f:validator validatorId="passwordValidator" />
<f:attribute name="passwordId" value="changepassform:password" />
</h:inputSecret>
</td>
</tr>
</h:form>
W drugim polu formularza podajemy walidator którym jest passwordValidator - jego implementacja zajmiemy się za chwilę. Jako atrybut o nazwie passwordId podajemy z formularza changepassword (to nazwa aktualnego formularza) pole password, czyli to powyżej.
Teraz czas na klasę walidującą.
PasswordValidator.java
package com.wookasz.blogspot.jsfmultivalidator;

import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;

public class PasswordValidator implements Validator {

public void validate(FacesContext context, UIComponent component, Object value)
throws ValidatorException {

// pobranie atrybutu przekazanego w formularzu
String passwordId = (String)component.getAttributes().get("passwordId");
// odnalezienie odpowiedniego komponentu o tej nazwie
UIInput passwordInput = (UIInput)context.getViewRoot().findComponent(passwordId);
// pobranie wartości z tego komponentu
String password = (String) passwordInput.getValue();
String confirm = (String) value;

// sprawdzanie poprawnosci i ew. zrzucenie wyjątku
if (!password.equals(confirm)) {
throw new ValidatorException(new FacesMessage("Hasła nie są identyczne"));
}
}
}
Mam nadzięję, że komentarze w kodzie wystarczą by wyjaśnić sprawę ;-)
Pozostaje jeszcze rejestracja walidatora w faces-config.xml:
<validator>
<validator-id>passwordValidator</validator-id>
<validator-class>
com.wookasz.blogspot.jsfmultivalidator.PasswordValidator
</validator-class>
</validator>

Wynik:

8 komentarzy:

  1. ostatnio zagryzając się w JSF troszkę - gryźliśmy to w ten sposób, że pierwszy komponent podpinaliśmy pod beana, który był jednocześnie walidatorem w takim formularzu. Ale ta metoda mnie bardziej przekonuje ;-)

    Tomasz Bartczak
    Racjonalny Developer

    OdpowiedzUsuń
  2. fajna rzecz, ostatnio też coś takiego robiłem ale hmmm troche naokoło ;).
    Mam tylko jedną sugestie.
    Mógłbyś umieścić pierwszy kod w <h:form id="changepassform">
    ...
    </h:form>
    Dwie linijki ale ułatwisz początkującym zrozumienie.
    Pozdrawiam

    OdpowiedzUsuń
  3. Dzięki Dominiku za komentarz;) Poprawka przyjęta i wykonana. Oczywiście miałeś racje, to był błąd z mojej strony, że tego nie umieściłem ;-)

    Pozdrawiam !

    OdpowiedzUsuń
  4. To może trochę off topic ale:

    Jakie znacie sensowen zastosowania backing beanów, (czyli takich, których się używa do podstawiania całych UIComponentów)? Każdą tak naprawdę własność UIComponentu można wyliczyć przez expression i nie ma potrzeby obsługiwać programowo całego UIComponent - ja widzę jedno - używanie różnych UIComponentów w tym samym miejscu.

    Tomasz Bartczak
    Racjonalny Developer

    OdpowiedzUsuń
  5. Jakiś czas temu bawiłem się JSF, ale...

    A ja rozdzieliłbym kontrolę na dwa typy - kontrola pojedynczego pola vs wielu pol w formularrzu - i obsługiwał je oddzielnie. Walidator pojedynczego pola wykonuje się przez validator, a całego formularza po jego zatwierdzeniu i walidacji w ziarnie zarządzanym (ang. backing bean). Wartości formularza przypisywane są do pojedynczego ziarna, albo zrobiłbym zależności między dwoma i wykonanie metody to wykonanie walidacji "biznesowej", np. dwa pola muszą mieć tą samą wartość, jak w przypadku pola z hasłem.

    Jacek Laskowski
    Notatnik Projektanta Java EE

    OdpowiedzUsuń
  6. Mnie się osobiście takie rozwiązanie Jacku nie podoba. To dawanie zadań komponentom które powinny mieć inne. Walidacją powinny zajmować się walidatory ;-)

    OdpowiedzUsuń
  7. To kwestia nazewnictwa czy funkcjonalności? Do sprawdzenia dokumentu używam kontroli ortografii a później jeszcze dokument trafia do recenzenta. To dwa różne typy kontroli - techniczna i merytoryczna (biznesowa). Tak ja to widzę. Poza tym z JSF dawno się pożegnałem, a ostatnia euforia Grailsem w ogóle wyczyściła mi pamięć o nim.

    Jacek

    OdpowiedzUsuń
  8. ok, ten punkt widzenia do mnie bardziej przemawia ;-)
    A co do Grails to dobrze, bo przez to sporo ciekawych rzeczy się u Ciebie w notatniku pojawia ^^

    OdpowiedzUsuń