OCPSoft.com - Simple SolutionsCommunity Documentation
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"; }
Returning "pretty:", without a URL-mapping ID will cause the current page to be refreshed.
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"; }
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:
Example 5.1. Navigating directly from <h:commandLink>
or <h:commandButton>
<url-mapping id="viewItem"> <pattern value="/store/#{ cat : bean.category }/#{ iid : bean.itemId }/" /> <query-param name="language"> #{ bean.language } </query-param> <view-id value="/faces/shop/item.jsf" /> <action>#{bean.loadItem}</action> </url-mapping>
Note that if the specified URL-mapping ID requires any parameters, the current values found in the required managed bean locations will be used when navigating.
<h:commandLink action="pretty:home"> Go home. </h:commandLink>
Navigating directly from a command component is most commonly used for refreshing a page:
<h:commandLink action="pretty:"> Refresh this page. </h:commandLink>
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.
Example 5.2. Redirecting from a custom Servlet
public class CustomRedirector { public void redirect(HttpServletRequest request, HttpServletResponse response, String mappingId, Map<String, String[]>... params) { PrettyContext context = PrettyContext.getCurrentInstance(request); PrettyURLBuilder builder = new PrettyURLBuilder(); URLMapping mapping = context.getConfig().getMappingById(mappingId); String targetURL = builder.build(mapping, params); targetURL = response.encodeRedirectURL(targetURL); response.sendRedirect(targetURL); } }
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>
MultiPageMessagesSupport
uses to
solve the redirect problem.