Learning JSF2: Navigation

Posted by: Max Katz on September 18, 2009

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 .. condition (evaluates to true/false) in each case, it’s possible to navigate to three different pages. This allows to use application state to determine where to navigate. It’s not longer necessary to have model objects return arbitrary strings and thus could eliminate having your back-end know anything about navigation.

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 condition false. In such case you would think that navigation shouldn’t take place. However, you still navigate to confirmation.xhtml because implicit navigation is used. First, the above case is matched but not selected as .. evaluates to false. Navigation continues to look for a match and because purchase() method returned a string value of “confirmation” is used by implicit navigation which matches a page with such name. Implicit navigation is used last.

Max Katz

About Max Katz

Max Katz is a Senior Systems Engineer at Exadel. He has been helping customers jump-start their RIA development as well as providing mentoring, consulting, and training. Max is a recognized subject matter expert in the JSF developer community. He has provided JSF/RichFaces training for the past three years, presented at many conferences, and written several published articles on JSF-related topics. Max also leads Exadel's RIA strategy and writes about RIA technologies in his blog, http://mkblog.exadel.com. He is an author of “Practical RichFaces” book (Apress). Max holds a BS in computer science from the University of California, Davis.

Why Attend the NFJS Tour?

  • » Cutting-Edge Technologies
  • » Agile Practices
  • » Peer Exchange

Current Topics:

  • Languages on the JVM: Scala, Groovy, Clojure
  • Enterprise Java
  • Core Java, Java 8
  • Agility
  • Testing: Geb, Spock, Easyb
  • REST
  • NoSQL: MongoDB, Cassandra
  • Hadoop
  • Spring 4
  • Cloud
  • Automation Tools: Gradle, Git, Jenkins, Sonar
  • HTML5, CSS3, AngularJS, jQuery, Usability
  • Mobile Apps - iPhone and Android
  • More...
Learn More »