Reputation: 22867
I want to use both InCell editing in DataTable and the validation. I know that the trivial validations can be solved with f:validator, but what with non-trivial names?
I must, let's say, assure that the 'name' property is unique in the table. So, before accepting the edit, I should check if the name is changed and if it is used by another element. If so, the edit must be refused.
How to achieve it? The eventListener would be only notified that the edit was accepted, as I have understood, so theoretically I could react and revert it, but I would prefer to refuse the edit when user clicks the 'accept' icon.
Upvotes: 3
Views: 7224
Reputation: 20369
Like Daniel said, you can use a JSF validator for this. A short example:
Assume we have a person:
public class Person
{
// Just imagine getters and setters ;-)
private String firstName, lastName;
}
And a very simple backing bean:
@ManagedBean
@ViewScoped
public class PersonBean
{
private List<Person> persons = new ArrayList<Person>();
@PostConstruct
private void init()
{
persons.add(new Person("John", "Doe"));
}
}
For example we want to ensure the first name starts with an uppercase letter. We don't care if the last name doesn't start with an uppercase letter (because of compatibility with IE or a legacy database, you know, the usual weirdness).
@FacesValidator("firstNameValidator")
public class FirstNameValidator implements javax.faces.validator.Validator
{
@Override
public void validate(FacesContext context, UIComponent component,
Object value) throws ValidatorException
{
if (!Character.isUpperCase(String.valueOf(value).charAt(0)))
{
FacesMessage msg = new FacesMessage("First name should start with a capital.");
throw new ValidatorException(msg);
}
}
}
Now to display everything nicely:
<p:growl id="growl" />
<h:form>
<p:dataTable value="#{bean.persons}" var="person" editable="true">
<p:ajax event="rowEdit" update=":growl"/>
<p:column headerText="first name">
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{person.firstName}" />
</f:facet>
<f:facet name="input">
<p:inputText validator="firstNameValidator"
value="#{person.firstName}" />
</f:facet>
</p:cellEditor>
</p:column>
<p:column headerText="last name">
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{person.lastName}" />
</f:facet>
<f:facet name="input">
<p:inputText value="#{person.lastName}" />
</f:facet>
</p:cellEditor>
</p:column>
<p:column>
<p:rowEditor />
</p:column>
</p:dataTable>
</h:form>
If you're interested, validation can be configured on domain-level by using bean validation (JSR-303). I strongly recommend it since it doesn't depend on JSF and it integrates with JPA.
Update as promised using bean validation:
First, the validator:
public class FirstNameValidator implements ConstraintValidator<FirstUpper, String>
{
@Override
public void initialize(FirstUpper constraintAnnotation) { }
@Override
public boolean isValid(String value, ConstraintValidatorContext context)
{
return Character.isUpperCase(value.charAt(0));
}
}
The annotation we're going to use:
@Constraint(validatedBy = FirstNameValidator.class)
@Target({ ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface FirstUpper
{
String message() default "{FirstUpper.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Note that the message we declared "{FirstUpper.message}"
will be resolved to a resource bundle. The bundle must be in the root of your classpath, named ValidationMessages.properties
. To localize you can add a locale code: ValidationMessages_en.properties
.
In that file declare the message:
FirstUpper.message=First name should start with a capital.
The person class:
public class Person
{
@FirstUpper
private String firstName;
private String lastName;
// Imagine the getters/setters again ;-)
}
And now, in your UI you don't have to refer to the validator, JSF is smart enough to validate using JSR-303. So instead of this:
<p:inputText validator="firstNameValidator" value="#{person.firstName}" />
Just use this:
<p:inputText value="#{person.firstName}" />
Easy right? ;-)
Upvotes: 6