views:

12405

answers:

4

Is there a way to create an html link using h:outputLink, other JSF tag or code to create a non faces request (HTTP GET) with request parameters?

For example I have the following navigation-rule

<navigation-rule>
    <navigation-case>
        <from-outcome>showMessage</from-outcome>
        <to-view-id>/showMessage.jsf</to-view-id>
        <redirect/>
    </navigation-case>
</navigation-rule>

In my page I would like to output the following html code:

<a href="/showMessage.jsf?msg=23">click to see the message</a>

I could just write the html code in the page, but I want to use the navigation rule in order to have all the urls defined in a single configurable file.

A: 

Have you considered a form?

<h:form>
    <h:commandLink value="Click to see the message" action="#{handler.outcome}" />
    <h:inputHidden id="msgId" value="#{bean.msgId}"/>
</h:form>
hubbardr
I have ;) but how am I gonna append the request paramters to the query string?
Panagiotis Korros
I don't know about JSF <h:form>, but this is automatic with an HTML <form method="get">.
Ed Brannin
+4  A: 

This is an interesting idea. I'd be curious to know how it pans out in practice.

Getting the navigation rules

Navigation is handled by the NavigationHandler. Getting hold of the NavigationHandler isn't difficult, but the API does not expose the rules it uses.

As I see it, you can:

  1. parse faces-config.xml on initialization and store the rules in the application context (easy)
  2. implement your own NavigationHandler that ignores the rules in faces-config.xml or supplements them with your own rules file and exposes its ruleset somehow (workable, but takes a bit of work)
  3. mock your own FacesContext and pass it to the existing navigation handler (really difficult to make two FacesContext object coexist in same thread and extremely inefficient)

Now, you have another problem too. Where are you going to keep the mappings to look up the views? Hard-code them in the beans?

Using the navigation rules

Off hand, I can think of two ways you could construct parameter-containing URLs from the back-end. Both involve defining a bean of some kind.

<managed-bean>
 <managed-bean-name>navBean</managed-bean-name>
 <managed-bean-class>foo.NavBean</managed-bean-class>
 <managed-bean-scope>application</managed-bean-scope>
</managed-bean>

Source:

package foo;

import java.io.IOException;
import java.io.Serializable;
import java.net.URLEncoder;

import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;

public class NavBean implements Serializable {

    private String getView() {
     String viewId = "/showMessage.faces"; // or look this up somewhere
     return viewId;
    }

    /**
     * Regular link to page
     */
    public String getUrlLink() {
     FacesContext context = FacesContext.getCurrentInstance();
     ExternalContext extContext = context.getExternalContext();
     String viewId = getView();
     String navUrl = context.getExternalContext().encodeActionURL(
       extContext.getRequestContextPath() + viewId);
     return navUrl;
    }

    /**
     * Just some value
     */
    public String getValue() {
     return "" + System.currentTimeMillis();
    }

    /**
     * Invoked by action
     */
    public String invokeRedirect() {
     FacesContext context = FacesContext.getCurrentInstance();
     ExternalContext extContext = context.getExternalContext();
     String viewId = getView();
     try {
      String charEncoding = extContext.getRequestCharacterEncoding();
      String name = URLEncoder.encode("foo", charEncoding);
      String value = URLEncoder.encode(getValue(), charEncoding);
      viewId = extContext.getRequestContextPath() + viewId + '?' + name
        + "=" + value;
      String urlLink = context.getExternalContext().encodeActionURL(
        viewId);
      extContext.redirect(urlLink);
     } catch (IOException e) {
      extContext.log(getClass().getName() + ".invokeRedirect", e);
     }
     return null;
    }

}

GET

For a GET request, you can use the UIParameters to set the values and let the renderer build the parameter list.

<h:outputLink value="#{navBean.urlLink}">
    <f:param name="foo" value="#{navBean.value}" />
    <h:outputText value="get" />
</h:outputLink>

POST

If you want to set the URL to a view during a POST action, you can do it using a redirect in an action (invoked by a button or commandLink).

<h:commandLink id="myCommandLink" action="#{navBean.invokeRedirect}">
    <h:outputText value="post" />
</h:commandLink>

Notes

Note that ExternalContext.encodeActionURL is used to encode the string. This is good practice for producing code that is portable across contexts (portlets, etcetera). You would use encodeResourceURL if you were encoding a link to an image or download file.

McDowell
Note that I wrote this answer before JSF2/JEE6 came out. JSF2 adds support for view parameters. Anyone stuck on JSF1.2/JEE5 should consider using Seam.
McDowell
A: 

You could use a commandLink with nested param tags. This is basically the same as hubbardr said above:

<h:form>
  <h:commandLink value="click here" action="${handler.outcome}">
    <f:param name="msgId" value="${bean.id}" />
  </h:commandLink>
</h:form>

Then in your backing bean you need to do:

Map requestMap = FacesContext.getCurrentInstance()
                 .getExternalContext().getRequestParameterMap();
String msgId = (String) requestMap.get("msgId");

And then do whatever you need to do.

Ian McLaird
A: 

Tried using PrettyFaces? It's a JSF extension designed specifically to make bookmarkable JSF pages / JSF with GET requests possible.

PrettyFaces - SEO, Dynamic Parameters, Bookmarks and Navigation for JSF / JSF2

Lincoln