I am looking for a little bit of guidance today with the issue I am running into.
What I am trying to accomplish is build a page on the fly with validation and all. The end result is to allow the user to configure the fields on the page through administrative functions. Below is a copy of the code that I am using as the test page where I loop through the "Configured" fields and write out the fields using the defined criteria.
<ui:repeat var="field" value="#{eventMgmt.eventFields}" varStatus="status">
<div class="formLabel">
<h:outputLabel value="#{field.customName}:"></h:outputLabel>
</div>
<div class="formInput">
<h:inputText id="inputField" style="width:# {field.fieldSize gt 0 ? field.fieldSize : 140}px;">
<f:validateRegex disabled="#{empty field.validationPattern}" pattern="#{field.validationPattern}"></f:validateRegex>
</h:inputText>
<h:message for="inputField" showDetail="true" errorClass="errorText"></h:message>
</div>
</ui:repeat>
After the page renders and I attempt to submit any values for the field, I get the following message "Regex pattern must be set to non-empty value." which obviously means that the expression is not populated. What makes it interesting to me is that fields that do not have an expression for them will be disabled when the EL is evaluated. I can also take the same code #{field.validationPattern} and put it in the page and the correct value will be written on the page.
So, my question(s) are:
1. Is this possible?
2. At what point does the JSF container look at binding the Pattern for the validate regex?
3. What am I doing wrong or What is the right way to do this ??
I am running Tomcat 7.0.22, Mojarra 2.1.5, and Eclipse as my IDE.
This is caused by using <f:validateRegex>
whose properties depend on the currently iterated item of <ui:repeat>
.
The <f:xxx>
tags are tag handlers, not UI components. Tag handlers are parsed and evaluated when the UI component tree is to be built during view build time. All EL is evaluated during the view build time. The <h:xxx>
tags and some <ui:xxx>
tags like <ui:repeat>
are UI components. All their EL is evaluated during view render time.
So in your case, when <f:validateRegex>
get parsed and executed, the #{field}
is not available in the current EL scope and thus evaluates as null
.
There are several ways to get it to work.
Move the validator to the class representing Field
and reference it as follows:
<h:inputText ... validator="#{field.validate}" />
with in Field
class wherein you manually instantiate it:
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
if (pattern != null) {
RegexValidator regexValidator = new RegexValidator();
regexValidator.setPattern(pattern);
regexValidator.validate(context, component, value);
}
}
Or, wrap #{eventMgmt.eventFields}
in a ListDataModel<Field>
and bind the validator to the #{eventMgmt}
bean. This way you will be able to set the validator's properties based on the row data:
<h:inputText ... validator="#{eventMgmt.validate}" />
with in the backing bean class behind #{eventMgmt}
:
private DataModel<Field> model;
private RegexValidator regexValidator;
@PostConstruct
public void init() {
regexValidator = new RegexValidator();
}
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
String pattern = model.getRowData().getPattern();
if (pattern != null) {
regexValidator.setPattern(pattern);
regexValidator.validate(context, component, value);
}
}
Or, create a custom Validator
which extends RegexValidator
and set the pattern as a custom attribute of the component by <f:attribute>
and let the Validator
intercept on that. The <f:attribute>
basically adds a new attribute to the component with an unevaluated ValueExpression
, so it will be re-evaluated when you call it. E.g.:
<h:inputText ...>
<f:validator validatorId="extendedRegexValidator" />
<f:attribute name="pattern" value="#{field.pattern}" />
</h:inputText>
with
@FacesValidator("extendedRegexValidator")
public class ExtendedRegexValidator extends RegexValidator {
@Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
String pattern = (String) component.getAttributes().get("pattern");
if (pattern != null) {
setPattern(pattern);
super.validate(context, component, value);
}
}
}
Or, if you happen to use JSF utility library OmniFaces, use its <o:validator>
. E.g.
<h:inputText ...>
<o:validator validatorId="javax.faces.RegularExpression" pattern="#{field.pattern}" />
</h:inputText>
Yes, that's all. The <o:validator>
will make sure that all attributes are evaluated as deferred expressions instead of immediate expressions.
See also:
- How to set converter properties for each row of a datatable?