1
votes

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)
1

1 Answers

1
votes

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.