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.
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();
// ...
}
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
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.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With