Setting f:setPropertyActionListener value with a f:param value

3.3k views Asked by At

I'm trying to use the setPropertyActionListener tag to set a value in my backing bean. However, it doesn't work as I expected.

Context: userService is an instance of my backing bean, which contains an int member, reqID. This, in turn, is the key to a map of objects that belong to a class called User. I'm trying to create a page that will list all instances of User, and provide a button to visit a separate view that shows that particular User's information. To do this, I'm attempting to set userService.reqID to the id of the chosen User so it can generate a reference to that user for the next view (which is done in the call userService.toUserInfo).

If I use the xhtml snippet below:

<ui:define name="content">
        <h:form>
            <h:panelGrid>
                    <ui:repeat value="#{userService.UserList.getUserList()}" var="user">
                        <li>
                            <h:outputText value="#{user.name}" />
                            <h:commandButton value="View details of #{user.name}" action="#{userService.toUserInfo}">
                            <f:param name="id" value="#{user.id}" />
                            <f:setPropertyActionListener target="#{userService.reqID}" value="#{id}"/>
                            </h:commandButton>

                        </li>
                    </ui:repeat> 
            </h:panelGrid>
        </h:form>
    </ui:define>

The tag does not appear to evaluate id correctly and I get a Null Pointer Exception.

Earlier, I tried changing my setPropertyActionListenerTag so it read out as:

<f:setPropertyActionListener target="#{userService.reqID}" value="id"/>

which gave me an error, because the tag was sending the string "id" as opposed to the int value of the parameter.

Is there some way to force f:setPropertyActionListener to evaluate the expression under value? Or is there another tag that will allow me to do this?

Also, is ui:param used appropriately here?

1

There are 1 answers

1
BalusC On BEST ANSWER

The <f:param> (and <ui:param>) doesn't work that way. The <f:param> is intented to add HTTP request parameters to outcome of <h:xxxLink> and <h:xxxButton> components, and to parameterize the message format in <h:outputFormat>. The <ui:param> is intented to pass Facelet context parameters to <ui:include>, <ui:decorate> and <ui:define>. Mojarra had the bug that it also behaves like <c:set> without a scope. This is not the intented usage.

Just use <c:set> without a scope if it's absolutely necessary to "alias" a (long) EL expression.

<c:set var="id" value="#{user.id}" />

Put it outside the <h:commandLink> though. Also in this construct, it's kind of weird. It doesn't make the code better. I'd just leave out it.

<f:setPropertyActionListener ... value="#{user.id}" />

See also:


Unrelated to the concrete problem, if you're using EL 2.2 (as you're using JSF 2.2, you undoubtedly are as it requires a minimum of Servlet 3.0, which goes hand in hand with EL 2.2), then just pass it as bean action method argument without <f:setPropertyActionListener> mess. See also a.o. Invoke direct methods or methods with arguments / variables / parameters in EL and How can I pass selected row to commandLink inside dataTable?

<h:commandButton ... action="#{userService.toUserInfo(user.id)}">

On again another unrelated note, such a "View user" or "Edit user" request is usually idempotent. You'd better use <h:link> (yes, with <f:param>) for this. See also a.o. Creating master-detail pages for entities, how to link them and which bean scope to choose and How to navigate in JSF? How to make URL reflect current page (and not previous one).

Oh, that <h:panelGrid> around the <ui:repeat><li> doesn't make sense in HTML perspective. Get rid of it and use <ul> instead. See also HTMLDog HTML Beginner tutorial.