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,
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 "";
}
}
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) {
try {
Object result = me.invoke(FacesContext.getCurrentInstance().getELContext(), null);
if (result instanceof String) {
return (String) result;
}
} catch (ValidatorException ve) {
throw ve;
}
}
return null;
}
}
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>
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
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"/>