However this validation does not happen concurrently with the other form validations.
A backing bean action method is indeed not intented to perform input validation.
Anyone have a solution?
Use the right tool for the job; use a normal Validator
.
@FacesValidator("dataRangeValidator")
public class DateRangeValidator implements Validator {
// ...
}
Validating multiple input values with a single validator is in turn however indeed a story apart. Basically, you should be grabbing/passing the other component or its value along into the validate()
method implementation. In its simplest form, you could use <f:attribute>
for this. Assuming that you're using <p:calendar>
to pick dates, here's a concrete kickoff example:
<p:calendar id="startDate" binding="#{startDateComponent}" value="#{bean.startDate}" pattern="MM/dd/yyyy" required="true" />
<p:calendar id="endDate" value="#{bean.endDate}" pattern="MM/dd/yyyy" required="true">
<f:validator validatorId="dateRangeValidator" />
<f:attribute name="startDateComponent" value="#{startDateComponent}" />
</p:calendar>
(note the binding
attribute, it makes the component available in the EL scope on exactly the given variable name; also note that this example is as-is and that you should absolutely not bind it to a bean property!)
Where the dateRangeValidator
look like this:
@FacesValidator("dateRangeValidator")
public class DateRangeValidator implements Validator {
@Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
if (value == null) {
return; // Let required="true" handle.
}
UIInput startDateComponent = (UIInput) component.getAttributes().get("startDateComponent");
if (!startDateComponent.isValid()) {
return; // Already invalidated. Don't care about it then.
}
Date startDate = (Date) startDateComponent.getValue();
if (startDate == null) {
return; // Let required="true" handle.
}
Date endDate = (Date) value;
if (startDate.after(endDate)) {
startDateComponent.setValid(false);
throw new ValidatorException(new FacesMessage(
FacesMessage.SEVERITY_ERROR, "Start date may not be after end date.", null));
}
}
}
If you happen to use JSF utility library OmniFaces, then you could also just use its <o:validateOrder>
component. The requirement can be achieved as follows without the need for a custom validator:
<p:calendar id="startDate" value="#{bean.startDate}" pattern="MM/dd/yyyy" required="true" />
<p:calendar id="endDate" value="#{bean.endDate}" pattern="MM/dd/yyyy" required="true" />
<o:validateOrder components="startDate endDate" />
See also: