2
votes

I have a JSF composite component util_primefaces:inplace_name that requires a "manager" backing bean that performs persistence updates when the 'name' field of an entity is edited (using p:inplace):

<cc:interface>
  <cc:attribute name="manager" type="com.example.web.AbstractManager" required="false" default="#{blockManager}"/>
  <cc:attribute name="element" type="com.example.entity.Element" required="true"/>
  <cc:attribute name="elid" required="true"/>
  <cc:attribute name="update" required="false" default="@parent"/>
 ..
</cc:interface>

<cc:implementation>
 ..
 <p:inplace id="#{cc.attrs.elid}" editor="true" emptyLabel="UNDEF" >
  <p:ajax 
   event="save" 
   listener="#{cc.attrs.manager.onInplaceNameSaveEvent}"
   process="@this #{cc.attrs.elid}-name"
   update="#{cc.attrs.update}"
  />
 <h:inputText id="#{cc.attrs.elid}-name" value="#{cc.attrs.element.name}"/>
 ..

Where for example @ViewScoped @ManagedBean BlockManager ultimately extends an AbstractManager, which has a listener method:

public void onInplaceNameSaveEvent(AjaxBehaviorEvent ae).

[ASIDE: the reason for the unusual "elid" attribute is described here, it plays no further role in this question: Primefaces p:inplace: How to more elegantly propagate the EL expression for entity to merge ]

When I invoke the composite component passing in an explicit #{blockManager} (or other subclass of AbstractManager) it works fine:

<util_primefaces:inplace_name 
 element="#{tenancy}" 
 elid="tenancy"
 manager="#{blockManager}"
/>

But if I don't pass in the #{blockManager}, on performing the inplace edit and save I get an error that the method onInplaceNameSaveEvent(AjaxBehaviorEvent) is not known:

<util_primefaces:inplace_name 
 element="#{tenancy}" 
 elid="tenancy"
/>

The error is:

WARNING: Method not found: [email protected](javax.faces.event.AjaxBehaviorEvent)
javax.el.MethodNotFoundException: Method not found: [email protected](javax.faces.event.AjaxBehaviorEvent)
at com.sun.el.util.ReflectionUtil.getMethod(ReflectionUtil.java:155) 

Q: Why is the backing bean not taken correctly using default="#{blockManager}" in the composite component attribute ?

1

1 Answers

5
votes

According to the documentation for the tag cc:attribute, the value for default must evaluate to a java.lang.String.

That's why the #{blockManager} expression is not working as you are expecting, you only can set defaults for String attributes.

To see what happens, you can test registering the following function in a taglib (like showed here):

public static String typeOf(Object o) {
    return o == null ? null : o.getClass().toString();
}

and use it in a composite component like this:

<ui:composition>
    <cc:interface>
        <cc:attribute name="param" type="java.lang.Integer" required="true"
            default="1" />
    </cc:interface>
    <cc:implementation>
        <h:outputText value="#{fnc:typeOf(cc.attrs.param)}" />
    </cc:implementation>
</ui:composition>

Now, if you use the component without specifying param, like this:

<my:comp />

... it will output class java.lang.String, because it uses the result of the ValueExpression for the default attribute, which is the String "1".

And when you specify param:

<my:comp param="1" />

... it outputs class java.lang.Integer, cause now it's the value resulting from coercing to the type specified for the cc:attribute.