JSF2.0 - Composite component with optional method expression
Asked Answered
D

5

8

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?

Danielledaniels answered 14/3, 2013 at 21:0 Comment(2)
You can pass optional attribute to actionListener. What is you use case, what do you think it should happen when actionListener is not resolved and commandLink is rendered?Hulse
Yes the link component is rendered, but when the click action happens in the link Component it complains that I have not defined a method for attribute actionMethod. But that is my intention, sometimes I don't want to define a action method for the actionMethod attribute. Is this possible? I thought putting the 'required = false' solve the problem.Danielledaniels
H
10

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>
Hulse answered 15/3, 2013 at 19:42 Comment(0)
P
7

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"/>
Pungy answered 9/8, 2016 at 8:56 Comment(3)
If using action instead of actionListener (as one typically should) this results in a FacesMessage warning Unable to find matching navigation case with from-view-id '...' for action '#{...}' with outcome 'null'. So you have to use actionListener for this. (Mojarra 2.3.3.SP1)Electromotive
@VsevolodGolovanov: try void action() instead.Mastermind
@BalusC, but wouldn't that also prevent an outcome from being considered, when there is an actual action method returning it?Electromotive
S
4

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;
    }
}
Scoliosis answered 22/1, 2014 at 13:56 Comment(0)
E
2

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 "";
    }

}
Eanore answered 30/6, 2015 at 10:59 Comment(0)
D
0

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

Doubledecker answered 24/7, 2018 at 16:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.