JSF expression evaluation of attribute tags

1.8k views Asked by At

I have a problem with evaluating EL expressions containing vars 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.

1

There are 1 answers

1
BalusC On

Any expression which contains a var created by another tag doesn't work. Datatables, dataiterators and most notably , e.g.

Tag handlers like <f:xxx>, <c:xxx> and some <ui:xxx> (those not having rendered 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 having rendered 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 in processValidators(). The parent component is just available by UIComponent#getParent() and its submitted value is in turn available by UIInput#getSubmittedValue().