Erick Alves
Erick Alves

Reputation: 354

JSF2.0 - Composite component with optional method expression

I'm implementing a composite component and I found a issue which I didn't find a solution.

I specified its attributes that can or not be passed by the page author, but I couldn't specify a method attribute (a method expression to Action) that, if it wasn't passed, the composite component doesn't use the method attribute in the composite:implementation tag.

Here my code:

<composite:interface>
    <composite:attribute name="namePrompt" required="true"/>
    <composite:attribute name="actionMethod" method-signature="java.lang.String  action()" required="false"/>
    <composite:attribute name="showComponent" default="false"/>
</composite:interface>

<composite:implementation>
    <div>
       <p:commandLink actionListener="#{cc.attrs.actionMethod}"
                      rendered="#{cc.attrs.showComponent}"
                      >
            <h:outputText value="#{cc.attrs.namePrompt}"/>    
       </p:commandLink>
    </div>
</composite:implementation>

When using it, I didn't specify the "actionMethod" attribute. Like this:

<util:foo namePrompt="SomeName" showComponent="true"/>

But I get the error message:

javax.faces.FacesException: Unable to resolve composite component from using page using EL expression '#{cc.attrs.actionMethod}'

Is there a way to do this?

Upvotes: 8

Views: 10557

Answers (5)

h2mch
h2mch

Reputation: 531

An other solution is to create an own component type, with a action method. Example:

<composite:interface componentType="myButton">
    <composite:attribute name="namePrompt" required="true"/>
    <composite:attribute name="actionMethod" method-signature="java.lang.String  action()" required="false"/>
    <composite:attribute name="showComponent" default="false"/>
</composite:interface>

<composite:implementation>
    <div>
       <p:commandLink actionListener="#{cc.action()}" rendered="#{cc.attrs.showComponent}">
          <h:outputText value="#{cc.attrs.namePrompt}"/>    
      </p:commandLink>
   </div>
</composite:implementation>

And the componentType has to look like:

@FacesComponent("myButton")
public class MyButton extends UINamingContainer {

    public MyButton () {
    }

    public String action() {
        MethodExpression me = (MethodExpression) this.getAttributes().get("actionMethod");
        if (me != null) {
            Object result = me.invoke(FacesContext.getCurrentInstance().getELContext(),     null);
            if (result instanceof String) {
                return (String) result;
            }
        }
        return null;
    }
}

Upvotes: 4

Avoid puting methods inside composite. If tou need to do that, put the class that have the method inside composite, and use it like this:

<composite:interface>
   <composite:attribute name="classWithMethod" 
                        method-signature="java.lang.String"/>
</composite:interface>

And the implementation:

<composite:implementation>
   <div>
      <p:commandLink 
         actionListener="#{cc.attrs.classWithMethod.actionMethod}">    
      </p:commandLink>
   </div>
</composite:implementation>

Worked for me! :D

Upvotes: 0

Thogor
Thogor

Reputation: 321

Change the method signature return type to java.lang.Object and add "null" as the default value.

<composite:interface>
    <composite:attribute name="namePrompt" required="true"/>
    <composite:attribute name="actionMethod" method-signature="java.lang.Object action()" required="false" default="null"/>
    <composite:attribute name="showComponent" default="false"/>
</composite:interface>

<composite:implementation>
    <div>
       <p:commandLink actionListener="#{cc.attrs.actionMethod}"
                      rendered="#{cc.attrs.showComponent}"
                      >
            <h:outputText value="#{cc.attrs.namePrompt}"/>    
       </p:commandLink>
    </div>
</composite:implementation>

Without method:

<util:foo namePrompt="SomeName" showComponent="true"/>

With method:

<util:foo actionMethod="#{someBean.someMethod()}" namePrompt="SomeName" showComponent="true"/>

Upvotes: 7

Jitesh Pramodray
Jitesh Pramodray

Reputation: 454

Had the same exact error and needed to have an optional action method on my component too.

So I have tried adding a default parameter on the composite attribute with the method-signature, pointing to a method on the corresponding FacesComponent class and it works great!

Component:

<composite:interface componentType="myButton">
    <composite:attribute name="namePrompt" required="true"/>
    <composite:attribute name="actionMethod" method-signature="java.lang.String  action()" required="false" default="#{cc.dummyAction}"/>
    <composite:attribute name="showComponent" default="false"/>
</composite:interface>

<composite:implementation>
    <div>
       <p:commandLink action="#{cc.attrs.actionMethod}"
                      rendered="#{cc.attrs.showComponent}"
                      >
            <h:outputText value="#{cc.attrs.namePrompt}"/>    
       </p:commandLink>
    </div>
</composite:implementation>

FacesComponent class:

@FacesComponent("myButton")
public class MyButton extends UINamingContainer {

    public MyButton () {
    }

    public String dummyAction() {
       return "";
    }

}

Upvotes: 2

partlov
partlov

Reputation: 14277

You will have to create two p:commandLink elements and render them conditionally according to definition of your parameter:

<p:commandLink actionListener="#{cc.attrs.actionMethod}" rendered="#{!empty cc.getValueExpression('actionMethod') and cc.attrs.showComponent}">
  <h:outputText value="#{cc.attrs.namePrompt}"/>
</p:commandLink>
<p:commandLink rendered="#{empty cc.getValueExpression('actionMethod')}">
  <h:outputText value="#{cc.attrs.namePrompt}"/>
</p:commandLink>

Upvotes: 10

Related Questions