I am trying to write a simple create user page. I want the user to be able to type a desired username and if the username is already taken, then an output text shows up and says "Username already in use".
Here is my xhtml page
<tr>
<td>Username: </td>
<td>
<p:inputText id="username" value="#{createUserManagedBean.username}" required="true" requiredMessage="Username is required.">
<p:ajax event="keyup" update="uniqueUsernameMessage"/>
</p:inputText>
</td>
<td>
<h:outputText id="uniqueUsernameMessage" value="Username already in use" rendered="#{!createUserManagedBean.checkUniqueUsername()}" />
</td>
</tr>
Here is my managed bean
public boolean checkUniqueUsername()
{
if(!StringUtils.isBlank(getUsername()))
{
UserDTO userDTO = new UserDTO();
userDTO.setUsername(username);
boolean result = getUserService().validateUniqueUsername(userDTO);
return result;
}
else
return false;
}
My issue is that the message is not updating for each keyup event. The service was being called, but the element was not changing whether or not it would display or not depending on the method result.
Using the rendered attribute is absolutely not the right way to validate an input component. You should be using a real Validator implementation. Therein you can in case of invalidation 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.
All in all, this should do:
<p:inputText id="username" value="#{createUserManagedBean.username}" ...>
<f:validator binding="#{uniqueUsernameValidator}" />
<p:ajax event="keyup" update="usernameMessage" />
</p:inputText>
<h:message id="usernameMessage" for="username" />
With
#ManagedBean
#RequestScoped
public class UniqueUsernameValidator implements Validator {
#EJB
private UserService userService;
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
if (value == null || value.isEmpty()) {
return; // Let required="true" handle.
}
String username = (String) value;
if (userService.findByUsername(username) != null) {
throw new ValidatorException(new FacesMessage(
FacesMessage.SEVERITY_ERROR, "Username already in use. Please choose another", null));
}
}
}
See also:
How to perform validation in JSF, how to create a custom validator in JSF
Please note that the validator is a #ManagedBean instead of a #FacesValidator because the #EJB could otherwise not be injected. But if you're not using EJBs and are manually creating service classes and fiddling with transactions yourself, then you could probably just keep it a real #FacesValidator:
#FacesValidator("uniqueUsernameValidator")
public class UniqueUsernameValidator implements Validator {
Which is then instead to be referenced as follows:
<f:validator validatorId="uniqueUsernameValidator" />
See also:
How to inject in #FacesValidator with #EJB, #PersistenceContext, #Inject, #Autowired
Related
In JSF 2.X, can I render a component only when the validation success?
In my application I have many fields that must be filled. These data can be imported from a WebService through a search key.
When the user enter a valid search key the system searches the other fields and render them with the new values. But when the user enter a nonexistent key (or any other validation error) the server generates a validation error but still renders the fields, thus losing any data that there were filled.
What I need is that the user can perform the query and that if the query does not return results, this does not affect any data that he has already entered.
Below is a code example. Thus, if the user has filled in the fields inside updateThisOnSuccess and just after making an attempt to query without success, the value that is filled in is not lost.
<h:inputText value="#{controller.searchWebService}" >
<f:ajax execute="#this" render="updateThisOnSuccess messages" />
</h:inputText>
<h:panelGroup id="updateThisOnSuccess">
<h:inputText value="#{controller.field}" />
<!-- other fields -->
</h:panelGroup>
Submit the field values to run the search also does not seem an option as this will cause need to validate the fields inside updateThisOnSuccess.
Note: I saw the answer given by #BalusC to a similar question, but this is different from what I'm wondering why, in that case, foo-holder is always rendered and foo is conditioning. It's not my case, since this approach would make the controls do not appear when the validation fails.
Try this
<h:panelGroup id="updateThisOnSuccess">
<ui:fragment rendered="#{not facesContext.validationFailed}">
<h:inputText value="#{controller.field}" />
<!-- other fields -->
</ui:fragment>
</h:panelGroup>
Plaase try this. The requirements are that you must implement model validations with Bean Validation and the search field must implement JSF validation if required.
If you write "123456" then data is returned, else nothing is returned and a message is printed.
The backing bean:
#Named
#ViewScoped
public class yourBean implements Serializable{
private static final long serialVersionUID = 1L;
#Size(min=2)
private String field01;
private String searchWebService;
public void saveF(){
System.out.println("save");
}
public void searchWebServiceF(){
Boolean successWS = ("123456").equals(this.searchWebService);
if(successWS){
this.setField01("WS data");
}else{
FacesContext.getCurrentInstance().
addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "WS fails", ""));
}
}
public String getSearchWebService() {
return searchWebService;
}
public void setSearchWebService(String searchWebService) {
this.searchWebService = searchWebService;
}
public String getField01() {
return field01;
}
public void setField01(String field01) {
this.field01 = field01;
}
}
In your page:
<h:form id="form01">
<h:messages id="message"/>
<h:inputText id="wsid" value="#{pruebasBorradorBean.searchWebService}">
<f:validateLength maximum="6"/>
<f:ajax execute="#form" render="#form" listener="#{pruebasBorradorBean.searchWebServiceF()}" />
</h:inputText>
<h:panelGroup id="thedata">
<h:inputText value="#{pruebasBorradorBean.field01}">
<f:validateBean disabled="#{param['javax.faces.source']!='form01:save'}"/>
</h:inputText>
<!-- other fields -->
</h:panelGroup>
<h:commandButton id="save" value="submit">
<f:ajax render="thedata message" execute="#this thedata" listener="#{pruebasBorradorBean.saveF()}"/>
</h:commandButton>
</h:form>
You can change the components that will be processed in render phase changing the Collection at getRenderIds() of PartialViewContext. According to documentation this Collection is mutable.
FacesContext.getCurrentInstance().getPartialViewContext().getRenderIds().remove("formName:updateThisOnSuccess");
To test this solution, I used this controller:
#Named
#ViewScoped
public class Controller implements Serializable {
private static final long serialVersionUID = 1L;
private final static List<String> LIST_VALID_WEB_SERVICE_SEARCHS =
Arrays.asList(new String[] {"foo", "bar"});
private String webServiceParameter;
private Integer field01;
public void searchWebService() {
if (LIST_VALID_WEB_SERVICE_SEARCHS.contains(getWebServiceParameter())) {
setField01(123);
} else {
FacesContext facesContext = FacesContext.getCurrentInstance();
facesContext.getPartialViewContext().getRenderIds().remove("formFields");
FacesMessage facesMessage = new FacesMessage("Search not found in WebService.");
facesMessage.setSeverity(FacesMessage.SEVERITY_ERROR);
facesContext.addMessage("formName:searchWebService", facesMessage);
}
}
public void submit() {
System.out.println("submitted");
}
// Getters and Setters
}
And used this view:
<h:form id="formSearch">
<h:inputText id="webServiceParameter" value="#{controller.webServiceParameter}">
<f:ajax execute="#this" render="formFields messages" listener="#{controller.searchWebService}" />
</h:inputText><br />
</h:form>
<h:form id="formFields">
<h:inputText id="field01" value="#{controller.field01}" required="true">
<f:validateLongRange minimum="2" maximum="345" />
</h:inputText><br />
<!-- other fields -->
<h:commandButton value="submit" action="#{controller.submit}">
<f:ajax render="#form messages" execute="#form" />
</h:commandButton>
</h:form>
<h:messages id="messages" />
You can do something like that:
<f:ajax execute="#this" render="#{controller.success} message"/>
where success is a String attribute that will be empty if the WS fails and will be "updateThisOnSuccess" if not .
Or you could get rid of the JSF validation mechanism for informing the user the WS has failed. Think of it, it is not really a validation of the Model. You could draw an icon beside the WS Id field in red color or something similar using a boolean flag attribute in the backing bean.
I have the following xhtml, validator, and managedBean:
<h:form id="form">
<ui:repeat var="item" value="#{myBean.usersEmail}" varStatus="status">
<p:inputText id="userEmail" value="#{item.email}">
<f:validator validatorId="MyValidator"/>
</p:inputText>
<p:commandButton value="++++" update=":form" action="#{myBean.addEmail()}" />
</ui:repeat>
</h:form>
#FacesValidator("MyValidator")
public class ValidationClass extends Validator {
#Override
public void validate(FacesContext ctx, UIComponent component, Object value) throws ValidatorException {
String email = value.toString();
EmailValidator validator = EmailValidator.getInstance();
if(StringUtils.isNotBlank(email) && !validator.isValid(email)) {
FacesMessage message = new FacesMessage();
message.setSeverity(FacesMessage.SEVERITY_ERROR);
message.setSummary("Email is not valid.");
message.setDetail("Email is not valid.");
ctx.addMessage("userEmail", message);
throw new ValidatorException(message);
}
}
}
#ManagedBean
public class MyBean{
#Getter
#Setter
List<UserEmail> usersEmail = new ArrayList<UserEmail>();
public void addEmail(){
usersEmail.add(new UserEmail());
}
}
public class UserEmail{
#Getter
#Setter
String email = "";
}
The email addition works fines until the first validation fail.
When this happens, all inputText components show the same values.
For example, first I add "user1#gmail.com", this works ok.
Then I add "user2#gmail.com", this also works ok.
Then I change "user1#gmail.com" to "", this throws a validation exception, which is shown on the screen, and everything is still ok.
But then I correct the "" with "user3#gmail.com" and submit, this time all inputText start showing "user2#gmail.com", even when I add a new InputText, which also shows "user2#gmail.com".
It seems that when the validation fail, all components inside ui:repeat get bound to the value of the last item. Any thoughts?
I changed my implementation to use the c:forEach tag from JSTL and now it's working fine, even on Mojarra 2.2.6, here it's what I did:
<c:forEach var="item" items="#{myBean.usersEmail}" varStatus="status">
<p:inputText id="id${status.index}" value="${item.email}" validator="MyValidator" />
<p:message for="id${status.index}" />
<p:commandButton value="+" update=":form" action="#{myBean.addEmail()}" />
</c:forEach>
I have a ajax event blur that validate if a email already exist on database and it works.
<h:outputText value="Email:" />
<p:inputText id="email" value="#{pessoaBean.pessoa.email}"
required="true" requiredMessage="Informar o email."
validatorMessage="Formato de email inválido" >
<p:ajax event="blur" listener="#{pessoaBean.verificaEmail}"
update="mensagens" />
<f:validateRegex
pattern="^[_A-Za-z0-9-\+]+(\.[_A-Za-z0-9-]+)*#[A-Za-z0-9-]+(\.[A-Za-z0-9]+)*(\.[A-Za-z]{2,})$" />
</p:inputText>
Bean Method:
public void verificaEmail() {
if (new PessoaDao().verificaEmail(pessoa) == true) {
FacesContext.getCurrentInstance().addMessage("formCadastrar:email",
new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error",
"email already exist"));
}
}
When I submit my form with commandbutton it passes through my ajax validaton and submit my form even it have my error message displayed on screen.
My button code:
<p:commandButton value="Cadastrar"
actionListener="#{pessoaBean.cadastrar}"
update=":formPrincipal:tabelaPessoas,formCadastrar"
oncomplete="if (!args.validationFailed){PF('dialogCadastrar').hide();} "
/>
What happened here?
Don't do the validation work in a listener method, as JSF has proper validators for that. Acording to the official docs:
Individual Validators should examine the value and component that they are passed, and throw a ValidatorException containing a FacesMessage, documenting any failures to conform to the required rules.
So let's implement the one you need:
#FacesValidator("emailValidator")
public class EmailValidator implements Validator{
#Override
public void validate(FacesContext context, UIComponent component,
Object value) throws ValidatorException {
if (new PessoaDao().verificaEmail(value.toString()) == true) {
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error",
"email already exist"));
}
}
}
}
Then, you only need to attach the validator to the input you're interested in:
<p:inputText id="email" value="#{pessoaBean.pessoa.email}"
required="true" requiredMessage="Informar o email."
validatorMessage="Formato de email inválido" validator="emailValidator" />
This could of course be improved, you could add the regex validation into the code itself and also implement your own e-mail input composite component to be reused, with the validator already included.
See also:
Custom validation in JSF 2
JSF Composite Component tutorial
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.
I would like to perform validation in some of my input components such as <h:inputText> using some Java bean method. Should I use <f:validator> or <f:validateBean> for this? Where can I read more about it?
The standard way is to implement the Validator interface.
#FacesValidator("fooValidator")
public class FooValidator implements Validator {
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
// ...
if (valueIsInvalid) {
throw new ValidatorException(new FacesMessage("Value is invalid!"));
}
}
}
The #FacesValidator will register it to JSF with validator ID myValidator so that you can reference it in validator attribute of any <h:inputXxx>/<h:selectXxx> component as follows:
<h:inputText id="foo" value="#{bean.foo}" validator="fooValidator" />
<h:message for="foo" />
Whenever the validator throws a ValidatorException, then its message will be displayed in the <h:message> associated with the input field.
You can also use EL in validator attribute of any <h:inputXxx>/<h:selectXxx> component wherein you reference a managed bean method having exactly the same method signature (the same method arguments) as Validator#validate(). I.e. taking FacesContext, UIComponent and Object arguments in this order.
<h:inputText id="foo" value="#{bean.foo}" validator="#{bean.validateFoo}" />
<h:message for="foo" />
public void validateFoo(FacesContext context, UIComponent component, Object value) throws ValidatorException {
// ...
if (valueIsInvalid) {
throw new ValidatorException(new FacesMessage("Value is invalid!"));
}
}
This is only useful if the validator needs to access another property present in the same managed bean. If it doesn't need to, then this approach is considered tight-coupling (poor practice thus), and you should split out the validator to its own class implementing the Validator interface.
You can also use <f:validator> taghandler, which would be the only way if you intend to attach multiple validators on the same component:
<h:inputText id="foo" value="#{bean.foo}">
<f:validator validatorId="fooValidator" />
</h:inputText>
<h:message for="foo" />
This will execute the #FacesValidator("fooValidator") shown above.
You can also use <f:validator binding> to reference a concrete validator instance somewhere in the EL scope, which can be specified and supplied the following way:
<h:inputText id="foo" value="#{bean.foo}">
<f:validator binding="#{fooValidator}" />
</h:inputText>
<h:message for="foo" />
#Named("fooValidator")
public class FooValidator implements Validator {
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
// ...
if (valueIsInvalid) {
throw new ValidatorException(new FacesMessage("Value is invalid!"));
}
}
}
Note that thus #Named is being used instead of #FacesValidator. The old #ManagedBean is also supported here instead of #Named. Historically, this was a trick in order to be able to use #EJB and #Inject in a validator. See also How to inject in #FacesValidator with #EJB, #PersistenceContext, #Inject, #Autowired
Or this way, which in turn can easily be supplied as a lambda:
<h:inputText id="foo" value="#{bean.foo}">
<f:validator binding="#{bean.fooValidator}" />
</h:inputText>
<h:message for="foo" />
public Validator getFooValidator() {
return (context, component, value) -> {
// ...
if (valueIsInvalid) {
throw new ValidatorException(new FacesMessage("Value is invalid!"));
}
};
}
Also here applies the same problem of tight-coupling when this validator doesn't need any other property from the same bean.
To get a step further, you can use JSR303 bean validation. This validates fields based on annotations. So you can have just a
#Foo
private String foo;
Without the need to explicitly register any validator in XHTML side. If you're using JPA for persistence, by default this validator will also be executed during insert/update in DB. Since it's going to be a whole story, here are just some links to get started:
Hibernate Validator - Getting started
JSF 2.0 tutorial - Finetuning validation
There's also a <f:validateBean> tag, but this is only useful if you intend to disable the JSR303 bean validation. You then put the input components (or even the whole form) inside <f:validateBean disabled="true">.
See also:
JSF doesn't support cross-field validation, is there a workaround?
How to perform JSF validation in actionListener or action method?