5
votes

I have a simple composite component with an optional ajax listener implemented with the richfaces a4j:ajax component.

Here is the interface definition :

<!-- INTERFACE -->
<composite:interface name="inplaceInput">
    <composite:attribute name="value" required="true" />
    <composite:attribute name="render" default="@this" />
    <composite:attribute name="ajaxListener" method-signature="void actionListener(javax.faces.event.AjaxBehaviorEvent)" />
</composite:interface>

The following code works fine using two duplicated blocks with opposite rendered condition :

<composite:implementation>
    <rich:inplaceInput value="#{cc.attrs.value}" rendered="#{!empty cc.attrs.ajaxListener}">
        <a4j:ajax event="change" render="#{cc.attrs.render}" execute="@this" listener="#{cc.attrs.ajaxListener}" />
    </rich:inplaceInput>
    <rich:inplaceInput value="#{cc.attrs.value}" rendered="#{empty cc.attrs.ajaxListener}">
        <a4j:ajax event="change" render="#{cc.attrs.render}" execute="@this" />
    </rich:inplaceInput>
</composite:implementation>

But I'd like to avoid the duplication because my component as to be far more complicated...

Using a rendered condition on the a4j:ajax was my first idea :

<rich:inplaceInput value="#{cc.attrs.value}">
    <a4j:ajax event="change" render="#{cc.attrs.render}" execute="@this" listener="#{cc.attrs.ajaxListener}" rendered="#{!empty cc.attrs.ajaxListener}"  />
    <a4j:ajax event="change" render="#{cc.attrs.render}" execute="@this" rendered="#{empty cc.attrs.ajaxListener}" />
</rich:inplaceInput>

But it doesn't work, and the error is quite obscure:

javax.faces.FacesException: Unable to resolve composite component from using page using EL expression '#{cc.attrs.ajaxListener}'
at com.sun.faces.facelets.tag.TagAttributeImpl$AttributeLookupMethodExpression.invoke(TagAttributeImpl.java:444)
at org.ajax4jsf.component.behavior.MethodExpressionAjaxBehaviorListener.processAjaxBehavior(MethodExpressionAjaxBehaviorListener.java:73)

I tried with a c:if handler because the tree doesn't need to have both components:

<rich:inplaceInput value="#{cc.attrs.value}">
    <c:if test="#{!empty cc.attrs.ajaxListener}">
        <a4j:ajax event="change" render="#{cc.attrs.render}" execute="@this" listener="#{cc.attrs.ajaxListener}" />
    </c:if>
    <c:if test="#{empty cc.attrs.ajaxListener}">
        <a4j:ajax event="change" render="#{cc.attrs.render}" execute="@this" />
    </c:if>
</rich:inplaceInput>

And then got another error (using ajaxListener="#{itemList.updateValue}"):

javax.el.PropertyNotFoundException: /pages/itemList.xhtml @71,81 ajaxListener="#{itemList.updateValue}": Property 'updateValue' not found on type com...ItemListManager

If the attribute was an EL returning a value, I could test a default value. I could also add a boolean attribute to tell if the listener is set... Or add a default method on a componentType bean doing nothing... But that's not pretty...

How can I test if a attribute is set without accessing it's value or method binding ?

Thanks in advance for any idea,

Rgds,

FM

1

1 Answers

5
votes

You may use cc.getValueExpression() from UIComponent class which will check if Value Expression is defined in the using page of your composite component, like this:

<c:if test="#{!empty cc.getValueExpression('ajaxListener')}">     
  <a4j:ajax event="change" render="#{cc.attrs.render}" execute="@this" listener="#{cc.attrs.ajaxListener}" />     
</c:if>