views:

1708

answers:

3

In my JSF/Facelets application, I want to dynamically generate a breadcrumb trail from a list of page IDs using a custom tag:

<foo:breadcrumbs trail="foo,bar,baz"/>

This should generate something like:

<h:commandLink action="foo" ... />
<h:commandLink action="bar" ... />
<!-- (etc.) -->

My code looks something like this:

<ui:repeat value="#{fn:split(trail, ',')}" var="key">
    <h:commandLink action="#{key}" ... /> 
</ui:repeat>

The problem with this code is that #{key} is interpreted as a method binding. However, I just want the string value of #{key} to be returned as the navigation outcome. How can I achieve this?


The only thing I could think of was creating a dummy managed-bean that has an outcome field and an action handler, and invoke it like so:

<h:commandLink action="#{dummy.click}" ...>
    <f:setPropertyActionListener target="#{dummy.outcome}" value="#{key}" />
</h:commandLink>

with the dummy class defined like so:

public class Dummy {

    private String outcome;

    public String click() {
        return outcome;
    }

    public void setOutcome(String outcome) {
        this.outcome = outcome;
    }

    public void getOutcome() {
        return outcome;
    }
}

That seems ugly though, and I don't know if it would work.

+1  A: 

Why don't you create a custom component which generates the h:commandLink objects programmatically? It would probably be the 'cleanest' solution.

Phill Sacre
That sounds like a good idea, but I'm not sure how to do that. Could you point me in the right direction?
harto
There are a number of tutorials on the internet. For example, the IBM developerWorks tutorials: http://www.ibm.com/developerworks/views/java/libraryview.jsp?search_by=nonbelievers:
Phill Sacre
I think a custom component might be overkill in this case, but if you go that route, a single control that encodes/decodes multiple links would do the trick.
McDowell
+2  A: 

A couple of ways spring to mind.

Option 1

Stick with commandLink and read the var directly from the request map in the action binding:

public String click() {
  FacesContext context = FacesContext.getCurrentInstance();
  ExternalContext extContext = context.getExternalContext();
  Map<String, Object> reqMap = extContext.getRequestMap();
  return (String) reqMap.get("uirepeatVar");
}

(Where the repeater has the attribute var="uirepeatVar".)


Option 2

Switch to an outputLink and build GET links on the server:

public List<String> getViewUrls() {
  List<String> views = Arrays.asList("/index.xhtml", "/idtable.xhtml");

  List<String> urls = new ArrayList<String>();
  FacesContext context = FacesContext.getCurrentInstance();
  Application application = context.getApplication();
  ViewHandler viewHandler = application.getViewHandler();
  for (String view : views) {
    String url = viewHandler.getActionURL(context, view);
    urls.add(url);
  }
  return urls;
}

View:

<ui:repeat value="#{breadcrumbBean.viewUrls}" var="url">
  <h:outputLink value="#{url}">#{url}</h:outputLink> <br />
</ui:repeat>
McDowell
Hmmm... I'd prefer not to use `outputLink`s. I seem to keep running into trouble when I mix-and-match `commandLink`s and `outputLink`s. I'd prefer to stick with `commandLink`s for my intra-application links
harto
Then use the first option - read the var directly from the request map.
McDowell
It strikes me that my post wasn't clear - I've edited it.
McDowell
Thanks, McDowell. That first option seems to work perfectly.
harto
+1  A: 

Since asking this question, I've discovered an obvious solution that is very simple to implement.

A method that is the target of a JSF action must accept no arguments and return a String. It turns out that the String class already has a method that matches this signature - toString()!

So, I've changed my UI loop to the following:

<ui:repeat value="#{fn:split(trail, ',')}" var="key">
    <h:commandLink action="#{key.toString}" ... /> 
</ui:repeat>

This allows the dynamically evaluated key to be the JSF action outcome, and doesn't require any additional classes or ugly hackery.

harto