4

we have a (in our oppinion) very simple scenario here. But we got stuck somehow on composite components and f:attribute tags. I'll try to keep the code as simple as possible.

Composite Component:

<cc:interface name="button">
    ...
    <cc:attribute
        name="actionListener"
        required="true"
        method-signature="void f(javax.faces.event.ActionEvent)"
        target="button"
        shortDescription="The action listener for this button." />
    ...
</cc:interface>

<cc:implementation>
    <ice:commandButton
        ...
        actionListener="#{cc.attrs.actionListener}"
        ...>

        <cc:insertChildren />
    </ice:commandButton>
</cc:implementation>

... now we use the component like this:

<ctrl:button
    ...
    actionListener="#{bean.method}"
    ...>
    <f:attribute name="objectId" value="#{someObject.id}" />
</ctrl:button>

Now we need to access the "objectId" attribute inside the action listener method. We already tried somethign like this:

public void method(ActionEvent event)
{
    ...
    event.getComponent().getAttributes().get("objectId");
    ...
}

But the attribute map doesn't contain the objectId. Is there anything wrong on this approach? What is the recommended way to solve this problem?

Would be nice if someone could help us out.

Thanks! SlimShady

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
SlimShady
  • 185
  • 3
  • 11

2 Answers2

5

This <f:attribute> hack is a leftover from JSF 1.0/1.1 when it was not possible to pass objects as additional arguments to command buttons/links. Since JSF 1.2 you are supposed to use <f:setPropertyActionListener> for this.

<ctrl:button action="#{bean.method}">
    <f:setPropertyActionListener target="#{bean.objectId}" value="#{someObject.id}" />
</ctrl:button>

Since EL 2.2 (which is standard part of Servlet 3.0 but is for Servlet 2.5 implementable with help of JBoss EL) you could instead even pass the whole object just as method argument:

<ctrl:button action="#{bean.method(someObject.id)}" />
Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Hey @BalusC :) We are migrating to JSF 2 currently and facing similar issues. I have two questions: `f:setPropertyActionListener` does require target and the spec TLD does not list a `name` attribute for the tag, so I guess you meant `target`? – Tim Brückner Jan 27 '12 at 17:08
  • @truemmer: Copypaste mistake, yes :) I fixed it. – BalusC Jan 27 '12 at 17:17
  • I was too slow for editing the second question .-) The next question is about your second solution: You need to have an attribute named "action" in the composite component interface providing a `target` also, am I right? Why does the JSF 2 spec require for action methods to have no parameters? Thanks for answering all this stuff here :) – Tim Brückner Jan 27 '12 at 17:24
  • 1
    @truemmer: The spec requirement is a leftover from old ages when EL 2.2 didn't exist. Don't take it too strict. Invoking action methods with arguments works perfectly fine with EL 2.2. As long as a valid [`MethodExpression`](http://docs.oracle.com/javaee/6/api/javax/el/MethodExpression.html) object can be created based on the EL syntax, JSF can invoke it. – BalusC Jan 27 '12 at 17:33
3

I managed to read an attribute passed into a cc within the following setup.

<test:inner>
    <f:attribute name="fAttribute" value="myAttributeValue" />
</test:inner>

<cc:implementation>
    <h:commandButton value="button" actionListener="#{testBean.actionListener}" >
        <f:attribute name="innerAttribute" value="innerAttributeValue" />
            <cc:insertChildren /> <!-- not necessary, I hoped it would pass the outer attribute --->
    </h:commandButton>
</cc:implementation>

public void actionListener(ActionEvent event) {
    event.getComponent().getNamingContainer().getAttributes().get("fAttribute") 
    // > myAttributeValue
    event.getComponent().getAttributes().get("innerAttribute") 
    // > innerAttributeValue
}

The trick was to search within the naming container of the button. Thus a cc is always a naming container you can be sure that you end up in the inner-component.

I'm not sure if that's the way it was intendet to be but as far as I figured out the naming containter collects such attributes for its children.

Q: Does anybody out there know if not passing the attributes into the button is considered as a bug within Mojarra/JSF?

Dennis Bayer
  • 109
  • 5
  • I'm not sure whether this should be considered a hack or not, but I like your solution as a temporary step during a migration to JSF 2. IMHO you should stick with the second approach of BalusC's solution in the long term. – Tim Brückner Jan 27 '12 at 17:49
  • I think I see 2 con in @BalusC solution: you cannot use the `action=#{myBean.myMethod(myParameter)}` with an `actionListener` parameter, that is sometimes useful, and you cannot use the `f:setPropertyActionListener` solution inside loop like `ui:repeat` as you have to bind the value with a unique bean property. – Xavier Portebois Feb 01 '12 at 17:39