How to validate multiple fields in JSF? - validation

I'm going through validation in JSF and I see lots of examples of very basic logic. Frankly, I put them in the same category where the HelloWorld examples go. I can't imagine placing error messages in xhtml files, using a separate validation method for each validated field or employing bean validation.
What I want to do, is have a single method on the backing bean that will execute validation for each field, logging error messages driven by keys in i18n property files.
Can this be done? If so, how do we register that method as validating method, how do we obtain submitted field values for evaluation, and how do we register error messages?

<h:inputText id="username" value="#{bean.username}" label="UserName" binding="#{bean.component}"/>
<h:message for="username" />
<h:commandButton value="Submit" action="#{bean.actionMethod}" />
In your bean class,
private UIComponent component;
public UIComponent getComponent() {
return component;
}
public void setComponent(UIComponent component) {
this.component = component;
}
public String actionMethod() {
if (!validate()) {
return null;
}
// do your action method logic
}
private boolean validate() {
FacesContext context = FacesContext.getCurrentInstance();
//do validation for your fields and add to faces messages
FacesMessage msg = new FacesMessage(severity, summary, detail);
context.addMessage(component.getClientId(), msg);
// do for other fields
return status;
}
Refer this to get component client id
How to add a message to a specific component from JSF backing bean

Related

Inputtext validator attribute set by an existing field in a bakedbean

I have some inputtext in a form which is managed by a bean #Named and i would like to centralize the information concerning this fields such as which validator is assigned to which field.
If i directly write the name of the #FaceValidator, it works.
<h:inputText validator="validatorLogin"/>
If i tried to put the name of the validator with a bean property such as String validatorLogin = validatorLogin.
It will throw an error in the .xhtml like "Expression must be a method expression but is a value expression".
If i try to still run the code it will throw the following exception "validator=#{bean.validatorLogin}: Method not found".
<h:inputText validator="#{bean.validatorLogin}"/>
I expect the validator name to be set in the bean and the bean to feed the validator id in the inputtext field. So all informations about the form are centralized in one bean.
As well is it dumb to do so or is it something that will make the code more organized ?
As the validator attribute documentation states, there is no way to provide a validatorId via bean property to this attribute:
validator: MethodExpression representing a validator method that will be called
during Process Validations to perform correctness checks on the value
of this component. The expression must evaluate to a public method
that takes FacesContext, UIComponent, and Object parameters, with a
return type of void.
You would normally either hard code a validatorId as you did in your first example, or a method expression (in your second example) that resolves to a method like this:
public void validatorLogin(FacesContext context, UIComponent component, Object value)
throws ValidatorException {
// ...
}
Of course it's up to this bean implementation how the input is validated then. If you want to combine both approaches, you can delegate validation to one (or multiple) validators known by ID in your validatorLogin method:
public void validatorLogin(FacesContext context, UIComponent component, Object value)
throws ValidatorException {
final Collection<String> validatorIds = determineValidatorIds(context, component);
for (String validatorId : validatorIds) {
Validator<Object> validator = context.getApplication().createValidator(validatorId);
validator.validate(context, component, value);
}
}
private Collection<String> determineValidatorIds(FacesContext context, UIComponent component) {
// return hard coded validatorIDs or determine them on arbitrary logic.
}
If you urgently need to provide a validatorId via bean property, you can do so by using the f:validator tag within your input component:
<h:inputText id="txt" value="#{myBean.textValue}">
<f:validator validatorId="#{myBean.arbitraryValidatorId}" />
</h:inputText>

How to validate empty and not repeated jsf [duplicate]

I have Bean validation working nicely in my application. Now I want to check that a new user does not choose a username that has already been chosen.
In the actionlistener I have the code that checks the database but how do I force the user to be sent back to the page they were on if they choose an already existing username?
Introduction
You can do it, but JSF ajax/action/listener methods are semantically the wrong place to do validation. You actually don't want to get that far in JSF lifecycle if you've wrong input values in the form. You want the JSF lifecycle to stop after JSF validations phase.
You want to use a JSR303 Bean Validation annotation (#NotNull and friends) and/or constraint validator, or use a JSF Validator (required="true", <f:validateXxx>, etc) for that instead. It will be properly invoked during JSF validations phase. This way, when validation fails, the model values aren't updated and the business action isn't invoked and you stay in the same page/view.
As there isn't a standard Bean Validation annotation or JSF Validator for the purpose of checking if a given input value is unique according the database, you'd need to homegrow a custom validator for that.
I'll for both ways show how to create a custom validator which checks the uniqueness of the username.
Custom JSR303 Bean Validation Annotation
First create a custom #Username constraint annotation:
#Constraint(validatedBy = UsernameValidator.class)
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE})
public #interface Username {
String message() default "Username already exists";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
With this constraint validator (note: #EJB or #Inject inside a ConstraintValidator works only since CDI 1.1; so if you're still on CDI 1.0 then you'd need to manually grab it from JNDI):
public class UsernameValidator implements ConstraintValidator<Username, String> {
#EJB
private UserService service;
#Override
public void initialize(Username constraintAnnotation) {
// If not on CDI 1.1 yet, then you need to manually grab EJB from JNDI here.
}
Override
public boolean isValid(String username, ConstraintValidatorContext context) {
return !service.exist(username);
}
}
Finally use it as follows in model:
#Username
private String username;
Custom JSF Validator
An alternative is to use a custom JSF validator. Just implement the JSF Validator interface:
#ManagedBean
#RequestScoped
public class UsernameValidator implements Validator {
#EJB
private UserService userService;
#Override
public void validate(FacesContext context, UIComponent component, Object submittedAndConvertedValue) throws ValidatorException {
String username = (String) submittedAndConvertedValue;
if (username == null || username.isEmpty()) {
return; // Let required="true" or #NotNull handle it.
}
if (userService.exist(username)) {
throw new ValidatorException(new FacesMessage("Username already in use, choose another"));
}
}
}
Finally use it as follows in view:
<h:inputText id="username" ... validator="#{usernameValidator}" />
<h:message for="username" />
Note that you'd normally use a #FacesValidator annotation on the Validator class, but until the upcoming JSF 2.3, it doesn't support #EJB or #Inject. See also How to inject in #FacesValidator with #EJB, #PersistenceContext, #Inject, #Autowired.
Yes you can. You can do validation in action listener method, add faces messages if your custom validation failed, then call FacesContext.validationFailed() just before return.
The only problem with this solution is, it happens after the JSF validation and bean validation. I.e., it is after the validation phase. If you have multiple action listeners, say listener1 and listener2: if your custom validation in listener1 failed, it will continue to execute listener2. But after all, you'll get validationFailed in AJAX response.
It's better to use action method instead of actionListener for this purpose. Then you can return null (reloads page that triggered the action) from this method if the username exists. Here's an example:
in the facelet:
<h:commandButton action="#{testBean.doAction}" value="and... Action"/>
in the bean:
public String doAction() {
if (userExists) {
return null;
} else {
// go on processing ...
}
}
If you want to provide feedback to end-user:
xhtml:
<p:commandButton value="Go" process="#this" action="#{myBean.checkEntity()}" oncomplete="if(args.validationFailed){PF('widgetOldInfoNotice').show();}"/>
<p:confirmDialog id="dialogOldInfoNotice" header="NOTICE" severity="alert" widgetVar="widgetOldInfoNotice">
-- feedback message--
<p:button value="Ok" onclick="PF('widgetOldInfoNotice').hide();"/>
</p:confirmDialog>
bean:
public String checkEntity() {
if (!dao.whateverActionToValidateEntity(selectedEntity)) {
FacesContext context = FacesContext.getCurrentInstance();
context.validationFailed();
return "";
}
return "myPage.xhtml";
}
You can define a navigation case in the faces-config.xml file. This will allow you to redirect the user to a given page depending on the return value of the bean.
In the example below a suer is redirected to one of two pages depending on the return value of "myMethod()".
<navigation-rule>
<from-view-id>/index.xhtml</from-view-id>
<navigation-case>
<from-action>#{myBean.myMethod()}</from-action>
<from-outcome>true</from-outcome>
<to-view-id>/correct.xhtml</to-view-id>
</navigation-case>
<navigation-case>
<from-action>#{myBean.myMethod()}</from-action>
<from-outcome>false</from-outcome>
<to-view-id>/error.xhtml</to-view-id>
</navigation-case>
</navigation-rule>

Understanding JSF-validators. Do I really need one in this case? [duplicate]

I have Bean validation working nicely in my application. Now I want to check that a new user does not choose a username that has already been chosen.
In the actionlistener I have the code that checks the database but how do I force the user to be sent back to the page they were on if they choose an already existing username?
Introduction
You can do it, but JSF ajax/action/listener methods are semantically the wrong place to do validation. You actually don't want to get that far in JSF lifecycle if you've wrong input values in the form. You want the JSF lifecycle to stop after JSF validations phase.
You want to use a JSR303 Bean Validation annotation (#NotNull and friends) and/or constraint validator, or use a JSF Validator (required="true", <f:validateXxx>, etc) for that instead. It will be properly invoked during JSF validations phase. This way, when validation fails, the model values aren't updated and the business action isn't invoked and you stay in the same page/view.
As there isn't a standard Bean Validation annotation or JSF Validator for the purpose of checking if a given input value is unique according the database, you'd need to homegrow a custom validator for that.
I'll for both ways show how to create a custom validator which checks the uniqueness of the username.
Custom JSR303 Bean Validation Annotation
First create a custom #Username constraint annotation:
#Constraint(validatedBy = UsernameValidator.class)
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE})
public #interface Username {
String message() default "Username already exists";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
With this constraint validator (note: #EJB or #Inject inside a ConstraintValidator works only since CDI 1.1; so if you're still on CDI 1.0 then you'd need to manually grab it from JNDI):
public class UsernameValidator implements ConstraintValidator<Username, String> {
#EJB
private UserService service;
#Override
public void initialize(Username constraintAnnotation) {
// If not on CDI 1.1 yet, then you need to manually grab EJB from JNDI here.
}
Override
public boolean isValid(String username, ConstraintValidatorContext context) {
return !service.exist(username);
}
}
Finally use it as follows in model:
#Username
private String username;
Custom JSF Validator
An alternative is to use a custom JSF validator. Just implement the JSF Validator interface:
#ManagedBean
#RequestScoped
public class UsernameValidator implements Validator {
#EJB
private UserService userService;
#Override
public void validate(FacesContext context, UIComponent component, Object submittedAndConvertedValue) throws ValidatorException {
String username = (String) submittedAndConvertedValue;
if (username == null || username.isEmpty()) {
return; // Let required="true" or #NotNull handle it.
}
if (userService.exist(username)) {
throw new ValidatorException(new FacesMessage("Username already in use, choose another"));
}
}
}
Finally use it as follows in view:
<h:inputText id="username" ... validator="#{usernameValidator}" />
<h:message for="username" />
Note that you'd normally use a #FacesValidator annotation on the Validator class, but until the upcoming JSF 2.3, it doesn't support #EJB or #Inject. See also How to inject in #FacesValidator with #EJB, #PersistenceContext, #Inject, #Autowired.
Yes you can. You can do validation in action listener method, add faces messages if your custom validation failed, then call FacesContext.validationFailed() just before return.
The only problem with this solution is, it happens after the JSF validation and bean validation. I.e., it is after the validation phase. If you have multiple action listeners, say listener1 and listener2: if your custom validation in listener1 failed, it will continue to execute listener2. But after all, you'll get validationFailed in AJAX response.
It's better to use action method instead of actionListener for this purpose. Then you can return null (reloads page that triggered the action) from this method if the username exists. Here's an example:
in the facelet:
<h:commandButton action="#{testBean.doAction}" value="and... Action"/>
in the bean:
public String doAction() {
if (userExists) {
return null;
} else {
// go on processing ...
}
}
If you want to provide feedback to end-user:
xhtml:
<p:commandButton value="Go" process="#this" action="#{myBean.checkEntity()}" oncomplete="if(args.validationFailed){PF('widgetOldInfoNotice').show();}"/>
<p:confirmDialog id="dialogOldInfoNotice" header="NOTICE" severity="alert" widgetVar="widgetOldInfoNotice">
-- feedback message--
<p:button value="Ok" onclick="PF('widgetOldInfoNotice').hide();"/>
</p:confirmDialog>
bean:
public String checkEntity() {
if (!dao.whateverActionToValidateEntity(selectedEntity)) {
FacesContext context = FacesContext.getCurrentInstance();
context.validationFailed();
return "";
}
return "myPage.xhtml";
}
You can define a navigation case in the faces-config.xml file. This will allow you to redirect the user to a given page depending on the return value of the bean.
In the example below a suer is redirected to one of two pages depending on the return value of "myMethod()".
<navigation-rule>
<from-view-id>/index.xhtml</from-view-id>
<navigation-case>
<from-action>#{myBean.myMethod()}</from-action>
<from-outcome>true</from-outcome>
<to-view-id>/correct.xhtml</to-view-id>
</navigation-case>
<navigation-case>
<from-action>#{myBean.myMethod()}</from-action>
<from-outcome>false</from-outcome>
<to-view-id>/error.xhtml</to-view-id>
</navigation-case>
</navigation-rule>

custom validator call on button through ajax

hi i wrote a custom a validator which gets the system name and compare it against the id in database, now i wanna apply a check if this value is exactly the same, user must be allowed to click the button and move on else some error message should be displayed. and i am really confused how to call the validator() on through ajax.
my view page code is
<h:commandButton action="sample?faces-redirect=true" value="submit">
<f:ajax execute="#{csample.UserValidator}" render="#form" >
<h:inputText name="idtext" value="#{csampleBean.id}" />
</f:ajax>
</h:commandButton>
and my custom validator
public void UserValidator(FacesContext context, UIComponent toValidate, Object value)
throws UnknownHostException, ValidatorException, SQLException, NamingException
{
java.net.InetAddress localMachine = java.net.InetAddress.getLocalHost();
String machine= localMachine.getHostName();
String query = "select * from USER_ where USER_ID = '"+machine+"'";
Context initContext = new InitialContext();
Context envContext = (Context)initContext.lookup("java:/comp/env");
DataSource ds = (DataSource)envContext.lookup("jdbc/myoracle");
Connection conn = ds.getConnection();
Statement stat = conn.createStatement();
//get customer data from database
ResultSet result = stat.executeQuery(query);
if (query==machine)
// what to do here
conn.close();
need some guidance
You need to create a class implementing the Validator interface. On validation fail, just throw a ValidatorException with a FacesMessage. JSF will then take care that the FacesMessage ends up in the right <h:message> associated with the input component.
You can register the custom validator to JSF by annotating it with #FacesValidator with therein the validator ID. You can reference it in <h:inputXxx validator> or <f:validator validatorId>.
Here's a kickoff example:
#FacesValidator("userValidator")
public class UserValidator implements Validator {
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
// ...
if (!valid) {
String message = "Sorry, validation has failed because [...]. Please try again.";
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, message, null));
}
}
}
Which is been used as follows (note: <h:inputText> does not have name attribute! instead use id; also note that your initial code snippet has some nesting which isn't making any sense):
<h:inputText id="idtext" value="#{csampleBean.id}" validator="userValidator">
<f:ajax render="idtextMessage" />
</h:inputText>
<h:message id="idtextMessage" for="idtext" />
<h:commandButton action="sample?faces-redirect=true" value="submit" />
See also:
How to perform validation in JSF, how to create a custom validator in JSF
Unrelated to the concrete problem, your JDBC code is leaking DB resources. Please fix that as well.

Disable validator via ajax

I have a simple request scoped entity / pojo which has a Enum and a String as properties.
public Enum Type
{
None,
Email,
Fax;
}
#ManagedBean(name = "testEntity")
#RequestScoped
public class TestEntity
{
private Type type; //Default = None
private String address;
//getter and setter
}
This Enum has a field 'Email' which identifies a e-mail address with a related address.
In JSF I now want to enable/disable a validator of a address InputText field regarding the currently selected type in a SelectOneMenu.
<h:form id="formId">
<p:selectOneMenu id="type" value="#{testEntity.type}>
<p:ajax event="change" update=":formId:address"/>
<f:selectItem itemLabel="E-mail" itemValue="Email"/>
<f:selectItem itemLabel="Fax" itemValue="Fax"/>
</p:selectOneMenu>
<p:inputText id="address" value="#{testEntity.address}">
<f:validator validatorId="emailValidator" disabled="#{testEntity.type != 'Email'}"/>
</p:inputText>
<!-- button to call bean method with testEntity as param -->
</h:form>
It is not working the validator is never active but the ajax call is working since I can see the change value in other fields.
That's unfortunately not possible. The <f:xxx> tags are taghandlers (not UI components) which run during view build time, not during view render time. So if it's disabled during building of the view, it'll always be disabled until the view is recreated (e.g. by new request or a non-null navigation).
You'd need to have a "global" validator which delegates further to the desired validator based on the type attribute.
E.g.
<p:inputText ... validator="#{testEntity.validateAddress}" />
with
public void validateAddress(FacesContext context, UIComponent component, Object value) throws ValidatorException {
if (type == Email) {
context.getApplication().createValidator("emailValidator").validate(context, component, value);
}
}
Update OmniFaces has recently added a new <o:validator> tag which should solve exactly this problem as follows:
<o:validator validatorId="emailValidator" disabled="#{testEntity.type != 'Email'}"/>
See the showcase example here.
Maybe someone is interested in how I solved it thanks to BalusC help.
Pass type component clientId to custom converter.
<f:attribute name="typeComponentId" value=":formId:type"/>
Validator:
public class TestEntity implements Validator
{
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException
{
final String typeComponentId = (String)component.getAttributes().get("typeComponentId");
final UIInput compType = (UIInput)context.getViewRoot().findComponent(typeComponentId);
if(compType != null)
{
final Type type = (Type)compType.getValue();
if(type == Type.Email)
new EmailValidator().validate(context, component, value);
}
}
}
Edit:
Not working inside a ui:repeat component such as p:datatable.

Resources