Introduction
Validators in JSF are nice. They, however, have its shortcomings. They will by default validate only one field at once. There is no standard way to attach one validator to multiple fields. Although there are some situations where you want this kind of functionality. For example validating the password confirmation field, validating the range of two numeric or date values (e.g. the one have to be lesser than the other), validating correctness of the three separate day, month and year fields, etcetera.
The cleanest solution would be to create a custom component which renders two or more components and use a specific validator for that, but that would involve more work. The easiest solution is to attach the validator to the last component of the group (components are rendered, validated, converted and updated in the same order as you define them in the JSF view) and pass the client ID of the other component(s) as unique f:attribute facet(s) along the last component. Then in the validator you can get the desired component(s) by the client ID using UIViewRoot#findComponent().
Back to top
Basic example
This example demonstrates a basic registration form with one username field and two password fields. The value of the second password field should equal to the value of the first password field before the action method may be invoked. The stuff is tested in a Java EE 5.0 environment with Tomcat 6.0 with Servlet 2.5, JSP 2.1 and JSF 1.2_07 (currently called Mojarra by the way!).
Here is the relevant JSF code. Note the f:attribute of the last password field, its value should contain the client ID of the first password field. Also note that the last password field doesn't have any valuebinding to the backing bean as this is unnecessary in this specific case.
<h:form id="register"> <h:panelGrid columns="3"> <h:outputLabel for="username" value="Username" /> <h:inputText id="username" value="#{myBean.username}" required="true" /> <h:message for="username" style="color: red;" /> <h:outputLabel for="password" value="Password" /> <h:inputSecret id="password" value="#{myBean.password}" required="true" /> <h:message for="password" style="color: red;" /> <h:outputLabel for="confirm" value="Confirm password" /> <h:inputSecret id="confirm" required="true"> <f:validator validatorId="passwordValidator" /> <f:attribute name="passwordId" value="register:password" /> </h:inputSecret> <h:message for="confirm" style="color: red;" /> <h:panelGroup /> <h:commandButton value="Register" action="#{myBean.register}" /> <h:message for="register" style="color: green;" /> </h:panelGrid> </h:form>
And now the validator code:
package mypackage; 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 { // Actions ------------------------------------------------------------------------------------ public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException { // Obtain the client ID of the first password field from f:attribute. String passwordId = (String) component.getAttributes().get("passwordId"); // Find the actual JSF component for the client ID. UIInput passwordInput = (UIInput) context.getViewRoot().findComponent(passwordId); // Get its value, the entered password of the first field. String password = (String) passwordInput.getValue(); // Cast the value of the entered password of the second field back to String. String confirm = (String) value; // Compare the first password with the second password. if (!password.equals(confirm)) { throw new ValidatorException(new FacesMessage("Passwords are not equal.")); } // You can even validate the minimum password length here and throw accordingly. // Or, if you're smart, calculate the password strength and throw accordingly ;) } }
The appropriate test backing bean look like:
package mypackage; import javax.faces.application.FacesMessage; import javax.faces.context.FacesContext; public class MyBean { // Init --------------------------------------------------------------------------------------- private String username; private String password; // Actions ------------------------------------------------------------------------------------ public void register() { // Just for debug. Don't do this in real! Hash the password, save to DB and forget it ;) System.out.println("Username: " + username); System.out.println("Password: " + password); // Show succes message. FacesContext.getCurrentInstance().addMessage("register", new FacesMessage("Succes!")); } // Getters ------------------------------------------------------------------------------------ public String getUsername() { return username; } public String getPassword() { return password; } // Setters ------------------------------------------------------------------------------------ public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } }
Finally the relevant part of the faces-config.xml:
<validator>
<validator-id>passwordValidator</validator-id>
<validator-class>mypackage.PasswordValidator</validator-class>
</validator>
<managed-bean>
<managed-bean-name>myBean</managed-bean-name>
<managed-bean-class>mypackage.MyBean</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
That's all, folks!
Back to top
Copyright - There is no copyright on the code. You can copy, change and distribute it freely. Just mentioning this site should be fair.
(C) December 2007, BalusC

27 comments:
How would you modify this example to support the case when you don't know the ID of the second field? For example, in my situation I have an editable datatable (just as described in one of your other posts) in which I wish to validate one of the input fields using another of the input fields in the same row. I've tried using
<h:inputText value="#{dataItem.attrib1}" required="#{detail.required}">
<f:attribute name="aParam" value="#{dataItem.someAttrib}"/>
<f:validator validatorId="myValidatorId"/>
</h:inputText>
where dataItem is defined earlier as:
<h:dataTable value="#{myBean.dataList}" var="dataItem">
in above code if the user changes the password and no the confirm password ... then the validation wont be called .. right ??
so ... one needs to add validation rules to both the inputText...
now if user modifies both password and confirmPassword then we will get two error messages not one ...
however what we need here is that user may modify either one or both of the password and there should be just one message displayed how do we support that ??
If your actual problem is that you have 2 identical messages if the user doesn't fill in the both password fields, then just change the 'required' attribute of the confirm password field as follows:
required="#{!empty param['register:password']}"
Why not just use Tomahawks standard validator for this (validateEqual)?
http://www.developersbook.com/jsf/myfaces/tomahawk-tag-reference/tomahawk-validateEqual.php
This also loosens up some of the tight coupling in this example
Because of 1) I didn't knew such a tag exist and 2) validateEqual is only useful if you want to validate 2 fields on equality, not if you want to validate multiple fields for some other reasons.
Ok, good reasons I guess. I didn't mean to trample on your solution. Actually, I learned quite a bit from it..
Let me just add that if you use the tomahawk validateEqual validator you can also define localized error messages to your form by entering them in your message bundle file with something like
org.apache.myfaces.Equal.INVALID=*
org.apache.myfaces.Equal.INVALID_detail=Passwords don't match
Thanks a lot!!!! This works perfectly.
Does anyone know of a way to validate that a series of fields are not null? I have 6 checkboxes that I need to make sure the user selects at least one of them
Use h:selectManyCheckbox with required="true".
I have a situation where I need to perform a simple validation check between a select field and an inputtext. The problem is, the second field is the input box and that is the field that I need to be sure has a value if the select field has a value. I can't throw the validation on the select field since the input field will always have null the first pass through and I can't place the validation on the input field since it will not be thrown if the field is left blank. Any ideas on how to do this?
Hiho this worked very well for me.
I searched a long time how to do this with only the jsf reference implementation.
Nice piece of code thanks a lot
Thanks a lot BalusC !
Your code is very helpful !
Thank You!
Great idea...
How do we write a JSF custom validator tag ???
Hi.
I have seen this tutorial, but I can't see how to attach to my page so it validate my fields before I submit the form.
let say we have this scenario.
2 or more hselectonemeny and 1 or more listbox..
I create a validator to control that the value in hselectonemenu1 < hselectonemenu2 and there is at least one element selected in listbox or something like that.
In my page how I call the validator???
Any comment??
Thanks
Hi.
After try several times I have my validator working ok, but I would like to know how to face the next thing:
Let's say I have to h:selectManyListBox related each other. One of them MUST be selected and the other UNSELECTED, so how can I access the values of the other component when I'm validating the other.
Example:
h:selectManyListbox01 validator="#{bean.validateSelectManylistbox01}"
h:selectManyListbox02 validator="#{bean.validateSelectManylistbox02}"
If I'm validating the selectManyListbox01 and nothing is selected I need to know if something in selectManyListbox02 is selected and the opposite.
I hope I have explain correctly
Any comment?
Thanks
Get the submitted value by UIInput#getSubmittedValue(). This will only work if its value isn't been processed yet during the validations phase.
Hi.
Sorry, but I didn't understand. Could you please be more specific?
Thanks
thx, have already learnt a lot with your helpful blog. Now I've a question concerning validation in a datatable build with ice:columns-tags enclosing just one ice:inputText-Tag. Have columns- and raw-Data-Models and build a table with several rows/columns. Now I have to validate the entries of two different cells together, means f.i. cell 1: start-time, cell 2: end-time--> validate that value of cell 1 is before value of cell 2. The problem for me is to specifie the cells, cause I only have one ice:inputText -field in my jsf-file: tried to use findComponent() with modified clientId() but failed, have read the invokeOnComponent-approach on the hookom's-blog (http://weblogs.java.net/blog/jhook/archive/2006/02/new_feature_for.html) but didn't really understood it. Could you give me some advise, please, here in the blog or by mail. Thx a lot for every hint. Here's some code to illustrate:
/ice:dataTable id="table" ...>
/ice:columns id="columns" value="#{myBean.columnsModel}" var="table">
/ice:inputText id="in" value="#{myBean.cellValue}" >
/f:validator validatorId="weekValidator" />
//ice:inputText>
//ice:columns>
//ice:dataTable>
(with /=<, (cause:"Tag is not allowed..." )
probably you'll need my eMail: here it is Seldon-X(ad)web.de
Hi Balusc,
I have an editable datatable (just as described in one of your other posts) in which I wish to validate one of the input fields using another of the input fields in the same row. I've tried using
>h:dataTable id="tableDS" binding="#{ctllisDSolicitud.dataTable}" value="#{ctllisDSolicitud.dataList}"
var="dataItem"
>h:inputText id="precio" value="#{dataItem.precio}" rendered="#{ctllisDSolicitud.editModeRow}" required="#{!empty param['ds:crudDS:tableDS:save']}" styleClass="input" size="10" >
>f:validateLength minimum="0" maximum="10" />
>val:commonsValidator type="required" arg="Monto" server="true" client="false"/>
>f:validator validatorId="validarpresupDisponible" />
>f:attribute name="idproy" value="ds:crudDS:idproyecto" />
>f:attribute name="idper" value="ds:crudDS:idperiodo" />
>f:attribute name="idpar" value="ds:crudDS:tableDS:0:partida" />
>/h:inputText>
When I run it i receive an exception like this:
Loading validation rules file from /org/apache/shale/validator/validator-rules.xml
Loading validation rules file from /WEB-INF/custom-rules.xml
validarpresupDisponible.validate: ds:crudDS:idproyecto 1
validarpresupDisponible.validate: ds:crudDS:idperiodo 4
executePhase(PROCESS_VALIDATIONS 3,com.sun.faces.context.FacesContextImpl@1f88c39) threw exception
Waiting for your reply
Thanks alot for sharing the knowledge.
Thanks! This example works great! I did have to change one thing with JSF 1.2 - I have to use UIInput.getSubmittedValue(), not getValue() in order to get the values for comparison for some reason :( But this was a really rich post -- I learned a lot looking through the code here, so definitely thanks!
You need the getSubmittedValue() whenever you want to get the value of a field which isn't converted/validated yet. The fields are converted/validated in the sequence as they appear in the form.
Thanks a lot BalusC! This example works great! I have an editable datatable, in which I wish to validate one of the input fields using another of the input fields in the same row. Any ideas on how to do this?
Waiting for your reply
Thanks alot for sharing the knowledge.
I totally agree. Currently, it seems like this is the way to go. But in general, using the id is somehow ugly : )
Hi BalusC,
First let me thank for the Code. However, I have a question.
What happens when user corrects the password he/she entered in password field to match the password in confirm field? I believe the validator will not be called and the error displayed on confirm field stays there, until the user clicks into confirm field and mouse out.
How to handle such a scenario?
Not true. The validation will only be fired when you submit the form to the server. The JSF validation is entirely server side. If you want client side validation, look in the Javascript/Ajax corner.
You didn't seem to answer the comparison of 2 values in a datatable.
As the fields are handled "in order" - I store the fields and on the validator of the last field in the row I do the actual comparison
so here goes ...
JSP
h:datatable value="{#mybean.anArray}" var="x"
h:inputText validator="{#mybean.validateA}" value="{x.valA
h:inputText validator="{#mybean.validateB}" value="{x.valB}"
h:datatable
Java
Date first, second;
public void validateA(FacesContext arg1, IUComponent arg2, Object arg3) {
first = (Date) arg3;
}
public void validateB(FacesContext arg1, IUComponent arg2, Object arg3) {
second = (Date) arg3;
if (! second.after(first)) {
// make faces message
throw new ValidatorException(msg)
}
}
Sorry if this not syntactially accurate, my development PC and internet PC are seperate and I am typing from memory.
Post a Comment