OCPSoft.com - Simple SolutionsCommunity Documentation

Chapter 5. Simplify Navigation

5.1. * Integrating with JSF action methods
5.2. * Integrating with JSF commandLink and commandButton
5.3. Redirecting from non-JSF application Java code
5.4. * Preserving FacesMessages across redirects

Navigation is a critical part of any web-application, and PrettyFaces provides simple integrated navigation - both with JSF, and in non-JSF environments (such as Servlets) that only have access to the HttpRequest object. PrettyFaces navigation is non-intrusive, and may be used optionally at your discretion.

Typically when navigating in JSF, one must define navigation cases (in JSF 1.x) or return the path to a view (in JSF 2.x). PrettyFaces, however, lets us simply reference the URL-mapping ID in these scenarios. Since the URL-mapping pattern already contains the locations of all required values (path-parameter EL expressions), these values are extracted from managed beans and used in URL generation.

Simply return a URL-mapping ID from any JSF action-method just as you would when using an <h:commandLink action="..." />. This outcome string should be prefixed by "pretty:" followed e.g.: "pretty:store", where "store" is our URL-mapping ID.

<url-mapping id="viewItem">
  <pattern value="/store/item/#{ iid : bean.itemId }/" />
  <view-id value="/faces/shop/item.jsf" />
  <action>#{bean.loadItem}</action>
</url-mapping>

Now look at our action-method; notice that if the item is found, we return null, signaling that no navigation should occur, but if the item cannot be loaded, we return "pretty:store" which is a PrettyFaces navigation outcome; PrettyFaces will intercept this outcome string, and redirect the user to the URL-mapping identified by "store" in pretty-config.xml.

public String loadItem() {
	if(itemId != null) {
		this.item = items.findById(itemId);
		return null;
	}

	// Add a message here, "The item {..} could not be found."
	return "pretty:store";
}

If any action method (JSF action-method or PrettyFaces page-actions) returns a mapping-ID that specifies path-parameters in the pattern, PrettyFaces will automatically extract values from bean locations specified in the pattern. The client browser will be redirected to the URL built using these values injected back into the pattern. For example:

<pattern value="/store/#{ cat : bean.category }/" />

Let's assume for a moment that #{ bean.category } contains the value, "shoes". if we return "pretty:viewCategory" from our action method, where "viewCategory" is the id of a URL-mapping in pretty-config.xml, PrettyFaces will extract the current value from the bean #{ bean.category } and use that value to generate a new URL:

/store/shoes/

This means that we can control the generated URLs directly from Java code. Simply set the value of the bean field (the field used in the URL pattern) to control the outcome:

public String loadItem() {
	...
	// Add a message here, "The item {..} could not be found."
	this.setCategory("shoes");
	return "pretty:viewCategory";
}

Tip

It is generally good practice to dedicate a separate bean to store page-parameters, this way, the same bean can be used throughout the application when setting values for PrettyFaces navigation.

It is also possible to navigate without needing a bean action method at all; this is done by referencing the URL-mapping ID in the command component directly. Similar to generating HTML links and URLs, for example:


Often there you might find yourself somewhere in a custom Servlet, or in a situation where you do not have access to the Faces NavigationHandler. For these situations, PrettyFaces provides the PrettyURLBuilder, which can be used to generate the String representation of any URL-mapping; one need only have access to an HttpServletRequest object in order to get the current configuration, and the current HttpServletResponse in order to issue a redirect.


Many users add FacesMessages to the FacesContext in their action methods to display them on the resulting page. The messages stored this way are saved in the the current request so that they are available during page rendering.

Adding messages to the page this way won't work anymore if your are returning an action outcome with the pretty: prefix. In this case the page won't be rendered in the same request but the user will be redirected to a completely new URL. As the messages added to the FacesContext are stored on request level they won't be available in the new request and so will get lost.

Fortunately there is a way to work around this issue. PrettyFaces contains a phase listener called MultiPageMessagesSupport that will save the FacesMessages before the redirect occurs and restore them on the next request.

To include the MultiPageMessagesSupport phase listener in your application, just add the following lines to your faces-config.xml.

<lifecycle>
  <phase-listener>com.ocpsoft.pretty.faces.event.MultiPageMessagesSupport</phase-listener>
</lifecycle>

Note

Please refer to this article for a detailed description of the way the MultiPageMessagesSupport uses to solve the redirect problem.