Enterprise Java

Spring & JSF integration: Dynamic Navigation

Often your JSF application will need to move beyond basic static navigation and start to make dynamic navigation decisions. For example, you may want to redirect users based on their age. Most JSF tutorials recommend that dynamic navigation is implemented by binding the action attribute of a command to a backing bean:

<h:commandButton action="#{bean.actionBasedOnAge}"/>
public String actionBasedOnAge() {
  if(age &lt; 12) {
    return "fetchadult";
  } else {
    return "ok"
  }
}

The example above shows how anyone under twelve is directed to 'fetchadult' instead of the usual 'ok'. Both the 'fetchadult' and 'ok' outcomes will need to have navigation rules defined in the faces-config.xml so that JSF knows what actual page to display.

When working with Spring MVC it is often more natural to have navigation logic contained in the @Controller bean. To help with this, implicit 'controller' and 'handler' variables are available when rendering JSF from MVC. The 'controller' variable provides access to the controller bean that was mapped to the original request, and the 'handler' variable to the underling MVC handler. In Spring 3.0 'controller' and 'handler' are generally the same object. In Spring 3.1 however, the underlying MVC architecture is changing and 'handler' will generally be a org.springframework.web.method.HandlerMethod instance.

Here is a submit button that references the someNavigation() method of the the @Controller:

<h:commandButton action="#{controller.someNavigation"/>

Whilst accessing the controller bean is useful, it is not the ideal solution. I prefer to use logical names in my JSF pages and map those the Java methods. I also want an easy way to get back to data from the underlying model.

The @NavigationMapping annotation provides another, more flexible approach to handling navigation. It works in a very similar way to @RequestMappings. The annotation can be placed on any public method in your @Controller to map navigation outcomes to destinations.

<h:commandButton action="submit"/>
@NavigationMapping
public String onSubmit() {
  return "redirect:http://www.springsource.org";
}

If you need access to a backing bean the standard Spring @Value annotation can be used. Any EL expression that the page can resolve can also be used on a navigation method parameter.

@NavigationMapping
public String onSubmit(@Value("#{person.age}") int age) {
...
}

Accessing model elements is even easier. As long as you only have a single object of the type you want to access in your model, and it is not a simple type (int, String, etc), you don’t need any annotations:

@NavigationMapping
public String onSubmit(Person p) {
...
}

Other argument types can also be used (see the JavaDoc for a complete list). For example, here is a navigation mapping that handles 'submit', 'cancel' and 'save' outcomes. The injected arguments tell us the which of the three outcomes was clicked and provides access to the source UIComponent.

@NavigationMapping('submit','cancel','save')
public String handleNavigation(String outcome, UIComponent source) {
...
}

Return types are also equally flexible. You can return view names as Strings, you can also use the same "@hotelsController.show" notation that I have previously blogged about. You can also return View objects directly or you can use NavigationOutcome if you want to include implicit model items.

Finally, if you just want to render an immediate response you can use the @ResponseBody annotation or return a HttpEntity. This works in exactly the same way as Spring.

Reference: Integrating Spring & JavaServer Faces : Dynamic Navigation from our JCG partner Phillip Webb at the Phil Webb’s Blog blog.

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button