4

I would like to add to my web app a dynamic breadcrumb using the Primefaces component. I've created a model to push items on the breadcrumb so that when one of its links is followed, the trailing links are removed. This works in most scenarios, but sometime the bradcrumb doesn't behave the way I expect. Basically, in order to track the landing page, I've added a preRenderView listener on each navigable page and implemented the model update logic in a session scoped bean.

  <f:event type="preRenderView" listener="#{bcb.onRenderView}" />
  <f:attribute name="pageName" value="ThisPage" />

The listener receives the page name as an attribute and obtains the complete URL (including query string) from the external context; these information, along with a unique id created from the UIViewRoot, are used to build a BreadCrumbItem that is pushed on the model:

public void onRenderView(ComponentSystemEvent evt) {
  UIViewRoot root = (UIViewRoot)evt.getSource();
  final String reqUrl = FacesUtils.getFullRequestURL();
  String pageName = (String) evt.getComponent().getAttributes().get("pageName");
  if(pageName != null) {
    model.push(new BreadCrumbItem(root.createUniqueId(), pageName, reqUrl));
  } else {
    model.reset();
  }
}

The push() and reset() methods of the model are implemented as follows:

/**
 * When a link is pushed on the bread crumb, the existing items are analyzed
 * and if one is found to be equal to the pushed one, the link is not added
 * and all the subsequent links are removed from the list.
 * 
 * @param link
 *            the link to be added to the bread crumb
 */
public void push(BreadCrumbItem link) {
    boolean found = removeTrailing(link);
    if(!found) {
        addMenuItem(link);
    }
}

/**
 * Reset the model to its initial state. Only the home link is retained.
 */
public void reset() {
    BreadCrumbItem home = new BreadCrumbItem();
    removeTrailing(home);
}

Is this approach feasible? Can you suggest some better way to track page navigation without the need to leverage a life cycle listener? Thanks a lot for your help.

anothernode
  • 5,100
  • 13
  • 43
  • 62
patrick
  • 111
  • 1
  • 5

1 Answers1

3

I have implemented my own one for my web app, in my case I didn't use the p:breadCrumb component because it's implemented using buttons.

Basically, I have an @SessionScoped bean which contains a stack (navigation stack), storing all the url's you have in the breadcrumb and the params for each of them. The view (xhtml) part is composed by p:button elements, which have the outcome of the stack's stored urls.

When you navigate to an url, corresponding bean's f:event type="preRenderView" is called (as the way you're doing it) and the bean takes the params from the url, after that it establishes itself into the stack (not the bean itself, cause it's @ViewScoped and going to be destroyed, just the url and params).

In case you click on a back button in the breadcrum, you send an additional param which indicates the index of the button. Based on that index, the destination bean knows that you're trying to recover such view, so asks the navigation stack for that view params and navigation stack removes navegables which are after it.

It took me a while, but it's fully functional. Good luck.

Edit

Be careful when using session scope for saving the current navigation state. It'll have influence in all the opened tabs, so probably it's not what you want, unless you expect your end user to use your application in one tab only. Anyway, the general usability guidelines say you should use categories instead of navigation paths for your breadcrumbs (HTTP is stateless and the history itself is kept by the browser). So a dynamic breadcrumb doesn't make sense anymore, at least if you're using different urls for your views.

Aritz
  • 30,971
  • 16
  • 136
  • 217