1
votes

we are still in a JSF 1.2 to 2.0 migration scenario and we are now facing a problem related to c:set or ui:param variables used inside an EL expression.

Here are the facts. There is a button as composite component:

<cc:interface name="button" shortDescription="A button.">
    ...
    <cc:attribute
        name="disabled"
        required="false"
        default="false"
        shortDescription="The disabled flag." />
    ...
</cc:interface>

<cc:implementation>
    <ice:commandButton
        ...
        disabled="#{cc.attrs.disabled}"
        ... />
</cc:implementation>

Now we are trying to use this button component inside a toolbar. The disabled state of the button is determined inside the toolbar using a c:set or a ui:param (we already tried both ways).

<c:set var="isButtonEnabled" value="#{backingBean.buttonEnabled}" />
or
<ui:param name="isButtonEnabled" value="#{backingBean.buttonEnabled}" />

#{isButtonEnabled}

<ctrl:button
    ...
    disabled="#{!isButtonEnabled}"
    ... />

So here is our problem. If we simple print out the value of "isButtonEnabled" in the toolbar, it is always correct. So the backing bean is ok. But when we try to pass this value to the composite component, it is not working. "Disabled" is always evaluated to false.

Sure we could pass the method expression directly (#{!backingBean.isButtonEnabled}) and this will work fine. But in our scenario the determination of the enabled-flag is much more complicated and I just tried to keep the example as simple as possible. Aditionally this flag is used for multiple buttons inside the toolbar, so we wanted to keep the code maintainable by using a c:set or ui:param. Is this the wrong way to handle this? What do you recommend?

Thanks in advance.

SlimShady

1

1 Answers

3
votes

Your problem is the way value binding is done in JSF. The prefered way is to retrieve the EL Expression an attribute was populated with by invoking getValueExpression("attributeName"). Then this EL Expression can be used to get or set the value in the backing bean. As your not passing #{!isButtonEnabled} but #{cc.attrs.disabled} to ice:commandButton the binding fails.

I solved this for the p:selectOneMenu component of Primefaces by writing a wrapping UIComponent which defines a property wrappedValue and passed that property to the p:selectOneMenu. In the getter and setter of that property I then used getValueExpression to retieve the real EL Expression for the attribute.

<composite:interface componentType="de.gw2tome.component.valuewrapper">
    <composite:attribute name="value" type="de.gw2tome.models.Rarity"
        required="true" />
</composite:interface>

<composite:implementation>
    <p:selectOneMenu value="#{cc.wrappedValue}"/>
    ...
</composite:implementation>

@FacesComponent("de.gw2tome.component.valuewrapper")
public class ValueWrapper extends UINamingContainer {

    public void setWrappedValue(Object wrappedValue) {
        ValueExpression expr = getValueExpression("value");
        ELContext ctx = getFacesContext().getELContext();

        expr.setValue(ctx, wrappedValue);
    }

    public Object getWrappedValue() {
        ValueExpression expr = getValueExpression("value");
        ELContext ctx = getFacesContext().getELContext();

        return expr.getValue(ctx);
    }
}

The component can now be used the following way:

<g:rarityChooser value="#{itemSearchBean.minRarity}" />