How to make JSF navigation rules more dynamic
Posted by Dominik
Consider the following situation:
You do a post request from some form in an JSF application and want to decide on the page to redirect to dynamically. No problem, just use different “navigation-case” entries and go to different “to-view-id”s depending on the “from-outcome”.
<navigation-rule>
<from-view-id>/pages/organisation/*</from-view-id>
<navigation-case>
<from-action>#{OrganisationBean.select}</from-action>
<from-outcome>to-user</from-outcome>
<to-view-id>/pages/user/details.jsf</to-view-id>
<redirect/>
</navigation-case>
<navigation-case>
<from-action>#{OrganisationBean.select}</from-action>
<from-outcome>to-project</from-outcome>
<to-view-id>/pages/project/details.jsf</to-view-id>
<redirect/>
</navigation-case>
<navigation-case>
<from-action>#{OrganisationBean.select}</from-action>
<from-outcome>to-whatever</from-outcome>
<to-view-id>/pages/whatever/details.jsf</to-view-id>
<redirect/>
</navigation-case>
</navigation-rule>Sure, this works. It’s not exactly what I would call DRY, but it works. But what if you need to be more dynamic then that?
Just use a framework that’s more dynamic I hear you say.
Sure, I’d love to. But what if that is not an option?
I found this, this and this on the web, and putting these together gave me the following solution which I wanted to share with you:
Step 1:
Create a ViewHandler, in my case a subclass of com.sun.facelets.FaceletViewHandler and overwrite getActionURL():
@Override
public String getActionURL(FacesContext context, String viewId)
{
String result = viewId;
if(Util.isVBExpression(viewId))
{
ValueBinding vb = context.getApplication().createValueBinding(viewId);
result = vb.getValue(context).toString();
}
result = super.getActionURL(context, value);
int queryStart = value.indexOf("?");
if((queryStart > 0) && (result.indexOf("?") == -1))
{
result = result + value.substring(queryStart);
}
return result;
}This enables you to use EL in the to-view-id entry of a navigation case.
Step 2:
Register your view handler with the application, in my case:
<application>
...
<view-handler>com.interactive_objects.jsf.crud.generic.DynamicViewHandler</view-handler>
</application>Step 3:
Now you can do things like:
<navigation-rule>
<from-view-id>/pages/organisation/*</from-view-id>
<navigation-case>
<from-action>#{OrganisationBean.select}</from-action>
<from-outcome>selected</from-outcome>
<to-view-id>#{OrgansiationBean.computedRedirect}</to-view-id>
<redirect/>
</navigation-case>
</navigation-rule>(You can also use compound expressions like “/path/to/page.jsf?foo=#{ManagedBean.bar}”).


Hi Dominik,
Your solution works like a charm. The only pitfall can be using prefix mapping /faces/ vs. extension mapping .faces. The latter can wreck your url using the super.getActionURL() as adds (via repleacment) teh “.faces” but this can be eliminated easily.
Coincidentally I had read all the solution articles you had mentioned myself, but yours gives the perfect sumary.
Thanks.
CF
PS: It’s a shame that JSF does not provide this by default. This solution can even be used passing parameters between pages when using navigation rules with as you are able to add the much needed parameters to your “redirect-GET-request”. JSF should just let request scoped beans survive a redirect since this is semantically one JSF request and only technically two (thanks to HTTP). JSF still has sooo much to be fixed before it really can be used productivly.
I am trying to implement this example in ADF w/ Facelets. So the few changes I had to make are as follows:
Registering the view handler is via ALTERNATE_VIEW_HANDLER within the web.xml (Instead of faces-config.xml)
getActionURL override had to be a little different as the default suffix we are defining seems to get slapped onto the end of the expression before it is passed to the method. So just a little trimming was needed.
Well, the viewHandler is being called and the expression is sucesfully converted from expression to path. However, after it leaves the overridden method I get a 404 Not Found for the given path. Any ideas?
Probably better to use the decorater pattern rather than tie it to a facelet implementation. This approach breaks the JSF pluggability architecture.
Hi Raghavan,
sure, the code above is a hack and we were already forced to use a decorator because we switched the implementations for testing. ;-)
(You can also use compound expressions like “/path/to/page.jsf?foo=#{ManagedBean.bar}”).
I can’t get this to work. If I try to this I only get redirected to /path/to/page.jsf
All the paramterers are gone.
I use facelets and wonder if I impored the right ViewHandler in my class that overrides the getActionURL
I get two suggestions when fixing imports in netBeans. javax.faces.application.ViewHandler; javax.faces.context.FacesContext ;
Witch one should I use?
Here is my class. —— 8< —- import com.sun.facelets.tag.jsf.core.ViewHandler; import com.sun.faces.util.Util; import javax.faces.application.ViewHandler; //import javax.faces.context.FacesContext; import javax.faces.el.ValueBinding;
public class DynamicViewHandler extends com.sun.facelets.FaceletViewHandler {
public DynamicViewHandler(ViewHandler parent) { super(parent); }
public String getActionURL(FacesContext context, String viewId) { String result = viewId; if(Util.isVBExpression(viewId)) { ValueBinding vb = context.getApplication().createValueBinding(viewId); result = vb.getValue(context).toString(); } return super.getActionURL(context, result); } }
Hello Dominik. I’ve tried it and it works cute. But what about situation, when we return null from backing bean method (there is no navigation used and page is re-rendered), and we’d like to keep request parameter for that page. I need it for my jsp-jsf application and I’d very grateful for any your suggestion:)
I have a problem, the default web.xml puts the JSF pages at /faces
the login.jsp page below simply does not register the /faces/login.jsp I also tried it with /login.jsp , same thing. any Ideas?
faces-config:
<faces-config>
</faces-config>
Hi,
Consider replacing the XML configuration files with a database-driven menu system.
hi, at the begining you say: “sure, this works.” i want to write what i do:
i wrote sth like these in faces-config.xml:
<navigation-rule> <from-view-id>*</from-view-id>
</navigation-rule>
i write this in a forum.jsp:
<h:outputText value=”#{myBean.loggedIn}”/>
my ifLoggedIn method is like:
public String ifLoggedIn(){ return loggedIn; }
i see “no” in the page forum.jsp.
but should not i redirect to login.jsp if the loggedIn value is “no”?
what is your “select” method is like?
how should my ifLoggedIn method be?
thanks
sorry it deletes some chars. my faces-config has lines like
navigation-rule from-view-id * from-view-id
{myBean.ifLoggedIn}
from-action
navigation-rule
Hi, I tried your solution and I can’t made it work with tomahawk, so I found a different solution you could try: http://javaxcross.blogspot.com/2009/10/dynamic-navigation-in-jsf.html
Although, I’m not work on this field, I actually want to say that your blog is very very good. Thank you for sharing the information about “How to make JSF navigation rules more dynamic”.
Search Engine Optimization Company for Link Building and PPC Management Services. Our Professional Team Ready for Web Designing and Social Media Optimization.
Cool man this was really informative to me great tutorial…
This is the most comprehensive guide I have come across. Thanks for sharing this with us! There are many things even experienced bloggers can learn from this post.Keep it up
This is very nice one and gives in depth information. I think it will be helpful. Thank you very much for that extraordinarily first class editorial! keep up the good work.