I have a problem with evaluating EL expressions containing var
s created by other tags.
I have a project where I am using a custom validator.
public class MyValidator implements Validator, StateHolder
I have a tag class associated with it:
public class MyValidatorTag extends ValidatorTag
this class allows for attribute fieldName
, with the appropriate tld
file for the tag:
<tag>
<name>my-validator</name>
<tag-class>my.packaga.MyValidatorTag</tag-class>
<body-content>JSP</body-content>
<description>This is my validator</description>
<attribute>
<name>fieldName</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<type>java.lang.String</type>
<description>This is some field I need</description>
</attribute>
</tag>
So far so good. But this setup doesn't allow one to use JSF EL expressions for attribute value. So the validator class (not tag class) I use this code to evaluate attribute value:
public static String evaluateEl(String expression) {
String value = null;
if (expression == null) {
return "";
}
if ((expression.indexOf("#{") != -1)
&& (expression.indexOf("#{") < expression.indexOf('}'))) {
Object evaledValue =
FacesContext.getCurrentInstance().getApplication().createValueBinding(
expression).getValue(FacesContext.getCurrentInstance());
if (evaledValue != null) {
value = evaledValue.toString();
} else {
value = null;
}
} else {
value = expression;
}
return value;
}
It is limited to strings, and it works for most EL expressions. Expression Hello #{1 eq 2}
will cause the attribute to have value Hello false
.
But there is a case for which this will not work. Any expression which contains a var
created by another tag doesn't work. Datatables, dataiterators and most notably <f:loadBundle>
, e.g.
<f:loadBundle basename="mypackage.message.ui-strings" var="msgs" />
Followed by an input component containing validator:
<cust:my-validator fieldName="#{msgs['myfield1.name']}"></cust:my-validator>
Fieldname evaluates to empty string using the expression evaluation code above. Changing it to #{requestScope.msgs....
doesn't fix the problem. Looking at loadBundle implementation, it adds the var msgs
to request scope, so I don't see why vars don't work for me. Please, somebody help me understand.
Tag handlers like
<f:xxx>
,<c:xxx>
and some<ui:xxx>
(those not havingrendered
attribute) are executed during JSF view build time, when a JSF UI component tree is to be produced. They are not part of the UI component tree. They have already done their job of producing the necessary JSF UI components.UI components like
<h:xxx>
and some<ui:xxx>
(those havingrendered
attribute) are executed during JSF view render time, when a large HTML string is to be produced which is to be sent to the HTTP response of the current HTTP request.So they don't run in sync.
There are for the enduser of the validator several ways to go around this, all are listed in this answer: How to set converter properties for each row of a datatable?
For the developer, there's another solution, let the validator extend
UIComponentBase
instead and perform the job inprocessValidators()
. The parent component is just available byUIComponent#getParent()
and its submitted value is in turn available byUIInput#getSubmittedValue()
.