Primefaces Hash Navigation Using jQuery BBQ
With jQuery BBQ you can keep track of state, history and allow bookmarking (#hashtag) while dynamically modifying the page via AJAX and/or DHTML.. just click the links, use your browser’s back and next buttons, reload the page, it’s all there.
Integrating jQuery BBQ with JSF/Primefaces is pretty much straight forward.
Before make sure that you have these points:
- Create your JSF Project
- Download jQuery BBQ (here)
- Include jQuery BBQ into your resources js folder
I’m going to use Primefaces extensions for the predefined layout’s sake (You can have your own menu and centered content layout).
Now the scenario is very simple, we have a master menu with links to our pages (outcome), we click a link the page doesn’t reload but reloads only the content of the centered container, and we have to make sure that this is bookmark-able and would work with the browser’s back and next buttons, and even if we reload the page .
I’m going to call our little project: Prime BBQ
The scenario should look like this
Create the main page.
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui" xmlns:pe="http://primefaces.org/ui/extensions"> <h:head> <h:outputStylesheet library="css" name="main.css" /> <h:outputScript library="js" name="jquery.ba-bbq.min.js" /> <h:outputScript library="js" name="main.js" /> </h:head> <h:body> <pe:layout id="page" fullPage="true"> <!-- West --> <pe:layoutPane id="west" position="west" > <f:facet name="header">Main Menu</f:facet> <h:form id="form1"> <p:panelMenu id="panelMenu"> <p:submenu label="Meat"> <p:menuitem outcome="/meat/steak.xhtml" value="Steak" /> <p:menuitem outcome="/meat/burger.xhtml" value="Burger" /> <p:menuitem outcome="/meat/chicken.xhtml" value="Chicken" /> <p:menuitem outcome="/meat/kebab.xhtml" value="Kebab" /> </p:submenu> </p:panelMenu> <p:remoteCommand name="updateNav" actionListener="#{mainBean.updateNav()}" update=":centerpanel"/> </h:form> </pe:layoutPane> <!-- Center --> <pe:layoutPane id="content" position="center"> <h:panelGroup id="centerpanel" layout="block"> <ui:include id="include" src="#{mainBean.currentNav}" /> </h:panelGroup> </pe:layoutPane> </pe:layout> </h:body> </html>
As you can see that we included jQuery BBQ into our page
<h:outputScript library="js" name="jquery.ba-bbq.min.js" />
And we have a main menu, with all our navigation rules.
Meat Master Menu
<p:submenu label="Meat"> <p:menuitem outcome="/meat/steak.xhtml" value="Steak" /> <p:menuitem outcome="/meat/burger.xhtml" value="Burger" /> <p:menuitem outcome="/meat/chicken.xhtml" value="Chicken" /> <p:menuitem outcome="/meat/kebab.xhtml" value="Kebab" /> </p:submenu>
In the center panel we have our center include.
<ui:include id="include" src="#{mainBean.currentNav}" />
this include would change based on the currentNav variable in our mainBean managedBean.
We have also a remoteCommand to change the currentNav to the current hashtag, and update our center panel include.
<p:remoteCommand name="updateNav" actionListener="#{mainBean.updateNav()}" update=":centerpanel"/>
Create and included a javascript file. For example main.js (make sure you include it after jQuery BBQ)
$(document).ready(function() { //handling the click on our menu items $('.ui-menuitem-link').click(function(event) { //making sure we don't navigate event.preventDefault(); //calculating our nav rule, this might change from app to app, this assume that your Faces Servlet is pointed to /faces/* var currentNav = $(this).attr('href').substr($(this).attr('href').indexOf("/faces") + 6); //set the hash in our window window.location.hash = '#' + currentNav; }) // Bind an event to window.onhashchange that, when the history state changes, // gets the url from the hash and displays fetches // new content to be displayed. $(window).bind('hashchange', function(e) { // Get the hash (fragment) as a string, with any leading # removed. Note that // in jQuery 1.4, you should use e.fragment instead of $.param.fragment(). var url = $.param.fragment(); //call our remoteCommand passing our current nav rule updateNav([{name: 'currentNav', value: url}]); }) // Since the event is only triggered when the hash changes, we need to trigger // the event now, to handle the hash the page may have loaded with. $(window).trigger('hashchange'); });
And lastly our MainBean
@ManagedBean @ViewScoped public class MainBean implements Serializable{ private String currentNav; public MainBean() { currentNav = "/meat/burger.xhtml"; //default page to load } public void updateNav() { FacesContext context = FacesContext.getCurrentInstance(); Map map = context.getExternalContext().getRequestParameterMap(); currentNav = (String) map.get("currentNav"); } public String getCurrentNav() { return currentNav; } public void setCurrentNav(String currentNav) { this.currentNav = currentNav; } }
For a working example, the project is available on Github
It’s very simple to find out any matter on net as compared to books, as I found this post at this
site.
Nice and smooth solution, thank you for sharing!
I’m using in my project with JSF Mojarra and Spring Security but not running.