1

I have a very simple JSF 2/Facelets page that looks like this:

<ui:repeat value="#{myBean.names}" var="_name">
  <h:commandLink value="#{_name}" action="#{myBean.sayHello(_name)}">
    <f:ajax execute="@this"/>
  </h:commandLink>
  <br/>
</ui:repeat>

The backing bean provides a java.util.List<String> with names and the action-method just prints a "hello <name>" message to standard output.

This works fine. I get a list of names in the browser and a click fires the action-method that says hello to the specified name.

The Problem arises, when I want to put this code in a composite component that does the iteration and renders the actual link via a facet:

<ui:component xmlns="http://www.w3.org/1999/xhtml"
              xmlns:f="http://xmlns.jcp.org/jsf/core"
              xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
              xmlns:cc="http://xmlns.jcp.org/jsf/composite">
  <cc:interface>
    <cc:attribute name="value" type="java.util.List" required="true" />
    <cc:facet name="content" />
  </cc:interface>
  <cc:implementation>
   <ui:repeat value="#{cc.attrs.value}" var="_name">
     <cc:renderFacet name="content"/>
   </ui:repeat>
  </cc:implementation>
</ui:component>

I use the composite component like this:

<my:myComp value="#{bean.names}">
  <f:facet name="content">
    <h:commandLink value="#{_name}" action="#{bean.sayHello(_name)}">
      <f:ajax execute="@this"/>
    </h:commandLink>
    <br/>
  </f:facet>
</my:myComp>

In the browser I get a list of names that looks exactly like before. But clicking a link now renders a "hello null" message. So _name is resolved correctly in the value attribute of <h:commandLink> but not in the action attribute.

I also tried using actionListener instead of action or the listener attribute from the <f:ajax> tag with no difference.

Could anybody shade some light on this issue?

My environment:

  • WildFly 8.1 with
  • JSF 2.2.6 (Mojarra)
Martin Höller
  • 2,714
  • 26
  • 44

1 Answers1

1

The issue has to do with the scope of the variable in this case _name which is evaluated once when the <ui:repeat/> is being processed. In my case, I ran your code and it produced Hello John even though their were other names in my list. To get around this, I introduced a <f:param/> that would contain the value of the _name, and modified your code as follows:

    <h:form>
        <my:myComp value="#{bean.names}">
            <f:facet name="content">
                <h:commandLink value="#{_name}" action="#{bean.sayHello()}">
                    <f:param name="name_" value="#{_name}"/>
                    <f:ajax execute="@this"/>
                </h:commandLink>
                <br/>
            </f:facet>
        </my:myComp>
    </h:form>

I also modified the sayHello() method as follows for a @RequestScoped bean:

@ManagedProperty(value = "#{facesContext}")
private FacesContext facesContext;

public void setFacesContext(FacesContext facesContext) {
    this.facesContext = facesContext;
}

public void sayHello() {
    Map<String, String> params = facesContext.getExternalContext()
                .getRequestParameterMap();
    String name = params.get("name_");
    System.out.println("Hello " + name);
}

You could change this to something shorter in a @ViewScoped bean to:

 public void sayHello() {
    Map<String, String> params = FacesContext.getCurrentInstance()
            .getExternalContext().getRequestParameterMap();
    String name = params.get("name_");
    System.out.println("Hello " + name);
}

The final result is that it prints out the names correctly.

John Yeary
  • 1,112
  • 22
  • 45
  • 1
    Thanks for your response. Your workaround is valid and that's actually how I have it already (wanted to post this later). Unfortunately you don't explain the details, why the variable gets resolved one time (for value) but not another time (for action). And that's what I'm actually interested in. Could you also provide this information, please? – Martin Höller Oct 31 '14 at 15:03
  • There is a very good explanation with examples here: [Values of h:inputText inside ui:repeat are not processed](http://stackoverflow.com/a/16550781/160361) – John Yeary Oct 31 '14 at 16:01
  • I'm quite sure, that's something different. My code works outside a composite component and doesn't call any setter. And the variable seems to be set as I can output it's value. Beside this, I tried using a wrapper like in your posted link with no difference in behavior. – Martin Höller Nov 03 '14 at 08:04
  • @JohnYearly, I gave you an upvote for the workaround. Still the actual question is not answered, yet. – Martin Höller Nov 04 '14 at 13:54