20

can I validate two interdependent fields in with one validator?

     <h:form>
            <h:inputText value="#{logRegBean.person.name}" >
                <f:validator validatorId="loginCorrectValidator" />
            </h:inputText>
            <h:inputSecret value="#{logRegBean.person.password}" />
            <h:commandButton action="#{logRegBean.login}" />
        </h:form>

I want to search for the user in the DB and if there is the user, I'll test if the passwords(in db and inputted) match. But how can I access even the password field in one validator? I tried to evaluate the value int the other field via createValueExpression(), but it looks like I can't access the value in that time since I always get empty strings.

ryskajakub
  • 6,351
  • 8
  • 45
  • 75

3 Answers3

28

Best what you can do is to grab the other UIInput component by UIViewRoot#findComponent() inside the validate() method and then determine the submitted value by either UIInput#getSubmittedValue() (when it occurs after the currently validated component in the component tree) or UIInput#getValue() (when it occurs before the current component and thus is already validated).

E.g.

public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
    UIInput otherInput = (UIInput) context.getViewRoot().findComponent("clientId");
    String otherValue = (String) otherInput.getSubmittedValue();
    // ...
}

See also:

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • 1
    i have a same problem. i added the example code in my code. But i got nullpointer exception. Can you say me why? – Kayser Aug 12 '13 at 15:33
  • 1
    @Kayser: Usually, you get that when the code attempts to access/invoke a reference which is actually `null`. See also http://docs.oracle.com/javase/7/docs/api/java/lang/NullPointerException.html – BalusC Aug 12 '13 at 15:35
  • DateIntervalValidator can't be null `public void validate(FacesContext context, UIComponent component, Object value) { UIInput fromComponent = (UIInput) context.getViewRoot().findComponent("from"); Date fromDate = (Date) fromComponent.getSubmittedValue();` ` ` – Kayser Aug 13 '13 at 07:34
  • 1
    @Kayser try findComponent(":from") where is the actual id of your form (ommit angular parenthesis). don't eat meat. – demonz demonz Feb 13 '14 at 16:51
  • Note that with absolute ID's you need a `:` before the ID in `UIViewRoot#findComponent()`. It seems to follow the same rules as the XHTML ID searches, according to the documentation, I think. – Andrew Oct 10 '17 at 12:52
  • In regards to `UIInput#getSubmittedValue()` and `UIInput#getValue()` see here: https://stackoverflow.com/a/46676231/1599699 – Andrew Oct 10 '17 at 21:37
11

The validation mechanism in JSF was designed to validate a single component.
However, in practice, you often need to ensure that related components have reasonable values before letting the values propagate into the model.
For example, it is not a good idea to ask users to enter a date into a single textfield.
Instead, you would use three different textfields, for the day, month, and year.

If the user enters an illegal date, such as February 30, you would like to show a validation error and prevent the illegal data from entering the model.

The trick is to attach the validator to the last of the components. By the time its validator is called, the preceding components passed validation and had their local values set. The last component has passed conversion, and the converted value is passed as the Object parameter of the validation method.

Of course, you need to have access to the other components. You can easily achieve that access by using a backing bean that contains all components of the current form. Simply attach the validation method to the backing bean:

public class BackingBean {

    private int day;
    private int month;
    private int year;

    private UIInput dayInput;
    private UIInput monthInput;
    private UIInput yearInput;

    // PROPERTY: day
    public int getDay() { return day; }
    public void setDay(int newValue) { day = newValue; }

    // PROPERTY: month
    public int getMonth() { return month; }
    public void setMonth(int newValue) { month = newValue; }

    // PROPERTY: year
    public int getYear() { return year; }
    public void setYear(int newValue) { year = newValue; }

    // PROPERTY: dayInput
    public UIInput getDayInput() { return dayInput; }
    public void setDayInput(UIInput newValue) { dayInput = newValue; }

    // PROPERTY: monthInput
    public UIInput getMonthInput() { return monthInput; }
    public void setMonthInput(UIInput newValue) { monthInput = newValue; }

    // PROPERTY: yearInput
    public UIInput getYearInput() { return yearInput; }
    public void setYearInput(UIInput newValue) { yearInput = newValue; }

    public void validateDate(FacesContext context, UIComponent component, Object value) {

       int d = ((Integer) dayInput.getLocalValue()).intValue();
       int m = ((Integer) monthInput.getLocalValue()).intValue();
       int y = ((Integer) value).intValue();

       if (!isValidDate(d, m, y)) {
          throw new ValidatorException(new FacesMessage("Invalid Date"));
       }

    }

    private static boolean isValidDate(int d, int m, int y) {
        //DO YOUR VALIDATION HERE
    }

 }

Here is your JSP

 <html>

   <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
   <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>

    <f:view>

       <head></head>

       <body>

          <h:form>

             <h:panelGrid columns="3">

                <h:inputText value="#{bb.day}"   binding="#{bb.dayInput}" size="2" required="true"/>

                <h:inputText value="#{bb.month}" binding="#{bb.monthInput}" size="2" required="true"/>

                <h:inputText value="#{bb.year}"  binding="#{bb.yearInput}" size="4" required="true" validator="#{bb.validateDate}"/>

                <h:message for="year" styleClass="errorMessage"/>

             </h:panelGrid>

             <h:commandButton value="Submit" action="submit"/>

          </h:form>

       </body>

    </f:view>

 </html>

Reference: Core JavaServer™ Faces By DAVID GEARY, CAY HORSTMANN

Publisher : Addison Wesley Pub Date : June 15, 2004 ISBN : 0-13-146305-5

Kerem Baydoğan
  • 10,475
  • 1
  • 43
  • 50
  • So what happens if you enter a valid date .e.g 28 feb 2017 but then change the day to 30? – DD. Sep 06 '17 at 21:52
1

I think SeamFaces' s:validateForm feature may be just what you need. (Seam Faces is a very useful library that brings some nifty CDI-based features to JSF.)

Nick
  • 2,827
  • 4
  • 29
  • 39
  • Can anybody provide or link infos about from where the `s:validateForm` is available after Seam has been discontinued and validation moved over to beanvalidation.org [http://seamframework.org/Seam3 ] – Kalle Richter Jun 19 '14 at 19:46
  • @KarlRichter as far as I know, form validation is not (yet) in Apache DeltaSpike, Seam's successor. For now, you can still use Seam's `s:validateForm` or RichFaces' `rich:graphValidator`. – Nick Jun 19 '14 at 21:41