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:


   page1.xhtml
   
       next
       /page2.xhtml
   

JSF 2 now supports implicit navigation where you don’t need to define a navigation rules inside JSF configuration file. You can do this:


JSF will try to find a view named page2.xhtml in the current directory.

The following will also work:


or


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):


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:


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:


   /pages/course.xhtml
   
      #{bean.register}
      success
      /pages/registered.xhtml
   

In JSF 2, you can now do this:


   /pages/course.xhtml
   
      #{bean.register}
      #{bean.prerequisiteCompleted}
      /pages/registered.xhtml
  
  
   #{bean.register}
   #{bean.advisingHold}
   /pages/scheduleAdvisingSession.xhtml
  
  
   #{bean.register}
   #{not bean.payment}
   /pages/payForCourse.xhtml
  

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:


   /bar/enter.xhtml
      
	enterBar
        /bar/welcome.xhtml
        
    

When using implicit navigation in JSF2, redirect is setup using faces-redirect=true request parameter:


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 :


   /el/page1.xhtml
   
      #{bean.navigate}
      success
      #{bean.page}
   

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:


   /purchase.xhtml
   
      #{bean.purchase}
      #{not bean.creditCardExpired}
     /confirmation.xhtml
   

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.

36 thoughts on “Learning JSF2: Navigation

  1. 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….

  2. Pingback: lost in thought » JSF2 Linksammlung

  3. 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.

  4. Pingback: Learning JSF2: Ajax in JSF – using f:ajax tag | Maxa Blog

  5. 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.

  6. @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.

  7. 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.

  8. @sam: implicit navigation takes precedence last, maybe you have another (defined) navigation rule that keeps you on the same page?

  9. 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?

  10. 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/*

  11. 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.

  12. 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.

  13. 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.

  14. Pingback: Learning JSF2: Using Flash scope | Maxa Blog

  15. Implicit Navigation is working fine -> shorter faces-config -> :-)

    But: Is there any command to stay on the same view using a new bean?
    My problem is: I’ve got View-Scoped-ManagedBeans, have a link where I will stay in the same viewbut will simulate the “Reload” of this view using a new (clean) View-Scoped-ManagedBean … at the moment I solved this problem with some “reset”-navigation-rules in my faces-config which is working fine but I like to shorten my faces-config as much as possible …

    Thanks in advance :)

  16. Sorry, next try,

    About faces-redirect,

    h:comandButton value=”Confirm” action=”#{userBean.addConfirmedUser}?faces-redirect=true”/>

    This gives: Not a Valid Method Expression

    How to fix?

  17. @Eric: I’m not sure whether redirect can be added this way. You can add it in navigation rules with redirect/ tag. Or, you can add ‘faces-redirect=true’ flag to the string returned by addConfirmedUser.

  18. Hi Max,

    In my application, I have common file containing menu and I am using implicit navigation where I don’t have to define a navigation rules inside JSF configuration file.

    I have following Dir structure

    index.html
    home.xhtml
    Pages/user/admin_page1.xhtml
    
    Pages/admin/user_page1.xhtml
    
    

    Here is MenuBean

    @ManagedBean(name = "menuBean")
    @ApplicationScoped
    public class MenuBean {
    
    	public String home() throws IOException {
    		return "home";
    	}
    
    	public String user_page1() throws IOException {
    		return "user/user_page1";
    	}
    
    	public String admin_page1() throws IOException {
    		return "admin/admin_page1";
    	}
    }
    

    But I am getting following message :
    Unable to find matching navigation case with from-view-id ‘/pages/user/user_page1.xhtml’ for action ‘#{menuBean.user_page1}’ with outcome ‘/admin/admin_page1′

    Please note that even though named directories based on Role, no security is implemented.

    Reagrds,

    Shirish

  19. @Shirish: the method you are clicking on is meneBean.user_page1 but the outcome based on error is /admin/admin_page1 — is that correct?

  20. hi,when using conditional navigation it happens that navigation cases are matched disregarding the from-action and from-outcome rules and navigation case is evaluated to only the expression contained in the “if”. The result of this is that the “return null” which I used for reloading the same page (for instance when I wanted to display validation errors, or to display the result of user search) is not working anymore, and instead of staying on the same page, the first clause that matches the if case is evaluated and thus I navigate wrongly to another page. Is this an issue of JSF2? please help, I cannot find a solution!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s