Learning JSF2: Navigation
This is a second post in Learning JSF 2 series. The first one on Managed Beans can be found here.
Before I start, thanks to Nick Belaevski (RichFaces Team Lead – Exadel) for reviewing this posting.
In JSF 1.2 all navigation rules are placed in JSF configuration file. Although you can still places navigation rules inside JSF configuration file, JSF 2 upgrades navigation by introducing implicit navigation and conditional navigation.
Implicit navigation
In JSF 1.2, navigating from one page to another required something like this:
<navigation-rule> <from-view-id>page1.xhtml</from-view-id> <navigation-case> <from-outcome>next</from-outcome> <to-view-id>/page2.xhtml</to-view-id> </navigation-case> </navigation-rule>
JSF 2 now supports implicit navigation where you don’t need to define a navigation rules inside JSF configuration file. You can do this:
<h:commandButton action="page2" value="Submit" />JSF will try to find a view named page2.xhtml in the current directory.
The following will also work:
<h:commandButton action="page2.xhtml" value="Submit" />or
<h:commandButton action="page2.jsf" value="Submit" />Note: this is assuming that JSF servlet is mapped to .jsf.
or using the new h:link (or h:button) tags in JSF 2 (I’ll cover these tags in a separate posting):
<h:button outcome="page2" value="Go There"/>Implicit navigation can also be used from within an action method:
public String next () { return "page2"; }
The following will also work:
public String next () { return "page2.jsf"; }
public String next () { return "page2.xhtml"; }
All examples above implied that both pages (page1.xhtml and page2.xhtml) are in the same directory (notice there is no / before page name). If pages are in different directories, then full path has to be used:
<h:commandButton action="/shopping/page2" value="Submit" />or
public String next () { return "/shopping/page2" ; }
Conditional navigation
In JSF 1.2, methods in managed beans would return arbitrary string values which are passed into the navigation. Navigation couldn’t use application state to help determine what page to select. The most you could is something like this, you could check what button/link was clicked in addition to using the outcome:
<navigation-rule> <from-view-id>/pages/course.xhtml</from-view-id> <navigation-case> <from-action>#{bean.register}</from-action> <from-outcome>success</from-outcome> <to-view-id>/pages/registered.xhtml</to-view-id> </navigation-case> </navigation-rule>
In JSF 2, you can now do this:
<navigation-rule> <from-view-id>/pages/course.xhtml</from-view-id> <navigation-case> <from-action>#{bean.register}</from-action> <if>#{bean.prerequisiteCompleted}</if> <to-view-id>/pages/registered.xhtml</to-view-id> </navigation-case> <navigation-case> <from-action>#{bean.register}</from-action> <if>#{bean.advisingHold}</if> <to-view-id>/pages/scheduleAdvisingSession.xhtml</to-view-id> </navigation-case> <navigation-case> <from-action>#{bean.register}</from-action> <if>#{not bean.payment}</if> <to-view-id>/pages/payForCourse.xhtml</to-view-id> </navigation-case> </navigation-rule>
When #{bean.register} action is invoked, based on
Forward/Redirect
When navigating to another page, both JSF 1.2 and JSF 2 perform a forward (default behavior) to the new page (Seam, for example does a redirect by default). Because it’s a server forward (the browser is not aware that we are displaying a different page), you might have noticed that the page address in the URL is always one behind.
If defining navigation rules in JSF configuration file, then the same tag is used in JSF 1.2 and JSF 2:
<navigation-rule> <from-view-id>/bar/enter.xhtml</from-view-id> <navigation-case> <from-outcome>enterBar</from-outcome> <to-view-id>/bar/welcome.xhtml</to-view-id> <redirect/> </navigation-case> </navigation-rule>
When using implicit navigation in JSF2, redirect is setup using faces-redirect=true request parameter:
<h:commandLink action="/bar/welcome?faces-redirect=true" value="Go to page 5"/>If returning an outcome from action method:
public String enter () { return "/bar/welcome?faces-redirect=true"; }
Using EL in to-view-id
You can also use EL in
<navigation-rule> <from-view-id>/el/page1.xhtml</from-view-id> <navigation-case> <from-action>#{bean.navigate}</from-action> <from-outcome>success</from-outcome> <to-view-id>#{bean.page}</to-view-id> </navigation-case> </navigation-rule>
In the managed bean:
… private String page; public String getPage () { return this.page; } public String navigate () { this.page = "/el/page3"; return "success"; } …
A topic closely related to navigation is page parameters and how they are propagated, I will cover that in another posting.
Finally, one thing to be aware of. Suppose you have the following rule:
<navigation-rule> <from-view-id>/purchase.xhtml</from-view-id> <navigation-case> <from-action>#{bean.purchase}</from-action> <if>#{not bean.creditCardExpired}</if> <to-view-id>/confirmation.xhtml</to-view-id> </navigation-case> </navigation-rule>
In managed bean:
public String purchase () { ... return "confirmation"; }
Suppose creditCardExpired evaluates to true and thus making



I have been following the tutorials on JSF 2.0 on this blog.
I appreciate the effort that makes Max Katz to,
keep us informed of developments and of what can be
do with RichFaces. Apropósito the testing I’ve done in
NetBeans 6.8 and works great ….
————————————————
He estado siguiendo los tutoriales sobre JSF 2.0 en este blog. Quiero agradecer el esfuerzo que hace Max Katz, para, mantenernos informados de las novedades y de lo que se puede hacer con RichFaces. Apropósito las pruebas las he hecho en NetBeans 6.8 y funciona de maravilla….
[...] Max Katz: Learning JSF2: Navigation (zweiter Teil der Einführungsserie) [...]
JSF 2.0.0 FCS is out!!!!
It is funny to see that the configuration hell that existed ever since struts became popular, finally is starting to fade away and make room for practicality. I understand why having the navigation in a nicely formatted XML is very convenient and has is pluses, but it’s simply not simple. I’ve always refused to start projects on struts because of these complexities (as I did for EJB 2 and do for Spring).
Everything is a balance.
[...] This is a third post in Learning JSF 2 series. The first one on Managed Beans can be found here and second one on navigation can be found here. [...]
good.I am learning JSF2 now.
JSF 2.0 is cool. However, the spec lead and expert group can go one step further by standardizing components such as calendar, list shuttle, data table with complex header and pagination, tree, etc, etc, …
Hopefully, the next release will address these long-awaited items.
@Doug: I don’t believe the EG wants to be in the business of building components. The want to define a standard with basic core features and tags that can be easily extendable. It’s up to 3rd party vendors such as RichFaces to provide a rich component set.
I am using JSF 2.0 and implicit navigation in a new project I am working on. The one issue I am facing is that some action methods navigate to the outcome page based on the return string but some don’t. They don’t even given an error.
For instance – this method does not work. It goes to the return “home”; line but does not do anything – seems like returns back to the same page – even though the home.xhtml is present in the same directory.
public String login() {
user = getUser();
if (user != null) {
if (!user.getPassword().equals(password)) {
JsfUtil.addErrorMessage(“Login Failed!. The password specified is not correct.”);
return null;
}
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put(USER_SESSION_KEY, user);
System.out.println(“Login Successful. Going to the welcome page …”);
if (!user.getRoleCode().equals(admin_role)) {
return “home”;
}else{
return “administration/index”;
}
}else{
JsfUtil.addErrorMessage(“Login Failed! User with ‘” emailAddress “‘ does not exist.”);
return null;
}
}
While this method – almost similar seems to perform the navigation just fine – sends the user to the index.xhtml
public String logout() {
HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(false);
if (session != null) {
session.invalidate();
}
return “index”;
}
There are couple of more instances where this is happening. In the same ManagedBean class one action method works but another does not. Any help would be appreciated.
@sam: implicit navigation takes precedence last, maybe you have another (defined) navigation rule that keeps you on the same page?
Thanks Max for a quick reply. Actually I don’t use the explicit rule definition in faces-config.xml at all. Also I did step-by-step debug through that and saw that it goes to a Method.invokeMethod() and just dies there.
Any idea what I might be missing?
@sam: does your method reach the return statement?
Max
Thanks a lot for responding to my problem and helping out.
Yes it does. the method goes to return statement and then steps into the Method.invokeMethod().
I changed the destination in the logout() to some other page in the same directory instead of index and it seemed to work fine.
The two places it does not work is places where I have some kinda put into session preceding the return like
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put(USER_SESSION_KEY, user);
Not sure why it should matter.
Another thing I wanted to point out is I have multiple url patterns in the web.xml (not sure if that should impact anything)
Faces Servlet
*.jsf
/faces/*
@sam: I’m not sure why it’s happening, try home.jsf or home.xhtml.
@sam: Also, Mojarra just released JSF version 2.0.3, see if it might solve your problem.
Thanks Max. I tried both of those options already and it does not seem to fix the issue. I have been banging my head on this for 3 days now
Appreciate your taking time so far to help.
@sam: have you tried Mojarra 2.0.3? I have no idea why it’s not working. What if you rename the page to home1.xhtml?
I tried Mojarra 2.0.3 and also renamed the file to homepage.xhtml and welcome.html to no avail. In fact it was called welcome.xhtml before and to debug this issue I renamed it to home.xhtml – maybe that left some reference to welcome.xhtml somewhere – don’t know where.
Surprisingly only two places this is happening. One is when I login and login validation is successful it is not going to homepage and the second is another object details page. Remaining 15 or so places it works just fine.
I looked at the com.sun.faces.application.NavigationHandlerImpl.java to see how it arrives at the destination to go and following the logic there it should find my page – but for some reason not.
Max, thanks for trying to help me. Finally nailed the sucker. I was using Primefaces instead and that was causing it to not work – yet to find out what it is. I am sure it is something stupid I have done. But changing it to made things work just fine.
Thanks again.
Interesting.. let me know when you find out.
[...] JSF will try to locate page called page2.xhtml and navigate to it. Navigation was was covered in this article. [...]