Reputation: 354
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
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
Reputation: 956
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
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
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
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