Recently a feature request was reported on our bug tracking system with the following description;
The bjsf:menubarItem enables the user to load new views in an application. Currently when a new view is loaded we cannot go back to the previous view using the browser back button. It would be great if of could have this feature.
Is this not something we can all ready do, without even tough the current bjsf:menubarItem implementation? The answer is yes! Within this article you find code snippets showing how to achieve this request.
Setup
The following snippet shows a part of a backing bean named; BehaviorBackingBean. Inside BehaviorBackingBean a method called loadPage will, when binded to an action event, load response.jsf into the UI component loadViewTarget.
private UIBackbaseOutputText testResult;
private UIComponent loadViewTarget;
private String page = "";
public void loadPage(SelectEvent event) {
String selectedMenuItem = ((UIBackbaseMenuItem) event.getComponent()).getItemLabel();
int page=0;
if(selectedMenuItem.equals("Load1")) { page=1;
} else if(selectedMenuItem.equals("Load2")) { page=2;
} else if(selectedMenuItem.equals("Load3")) { page=3;
}
BackbaseContext context = BackbaseContext.getCurrentInstance();
this.page = String.valueOf(page);
context.loadView(loadViewTarget, "/pages/behavior/response.jsf", "replaceChildren");
}
The next snippet shows the contents of Response.jsf. It contains a bjsf:view tag wrapping a bjsf:outputText component. Where this component is binded to the page property of the BehaviorBackingBean. This makes it possible to generate different responses with only one jsp file.
<bjsf:view>
<bjsf:outputText value="#{behaviorBean.page}" />
</bjsf:view>
</f:subview>
The following code snippet show some view application code with a bjsf:menuBar where some of the child bjsf:menuItem’s are supplied with a selectListener, which binds them to the loadPage method in BehaviorBackingBean. The bjsf:panelGroup is binded to the loadViewTarget, so it will be updated with a response once a menu item is selected.
<bjsf:menuItem itemLabel="File">
<bjsf:menuItem itemLabel="Load1" selectListener="#{behaviorBean.loadPage}" behavior="hm:addItemActionToHistory"/>
<bjsf:menuItem itemLabel="Load2" selectListener="#{behaviorBean.loadPage}" behavior="hm:addItemActionToHistory"/>
<bjsf:menuItem itemLabel="Load3" selectListener="#{behaviorBean.loadPage}" behavior="hm:addItemActionToHistory"/>
</bjsf:menuItem>
</bjsf:menuBar>
<bjsf:panelGroup id="target" binding="#{behaviorBean.loadViewTarget}"></bjsf:panelGroup>
Now everything has been setup, we jump to the actual part of explaining how to add client history management. Besides the selectListener attribute, there are two more attributes on the menu items, namely; itemLabel, the label rendered in the view and behavior, which basically adds generic instructions to a client control, see the client manual for a more detailed explanation of behaviors. The next snippet contains the code for the behavior hm:addItemActionToHistory:
Behavior
<d:behavior name="addItemActionToHistory">
<d:resource type="text/javascript"><![CDATA[
var oComp = this;
bb.bookmarkComp = function(sMenuItem) {
if(sMenuItem.indexOf('menuItem') == 0){
var sId = sMenuItem.substring(9);
bb.command.fireEvent(bb.document.getElementById(sId), 'click', true, false);
return true;
}
return false;
}
bb.document.addEventListener('history', function(event) { bb.bookmarkComp(event.bookmark); }, false);
]]></d:resource>
<d:handler event="click" type="text/javascript"><![CDATA[
bb.history.add('menuItem:' + this.getAttribute('id'), this.getAttribute('label'));
]]></d:handler>
</d:behavior>
</d:namespace>
For the purpose of this example the behavior is defined in its own namespace, e.g.; http://www.backbase.com/sample/history with prefix hm. Inside the behavior there is a a tag d:resource which contains the definition of a bookmarkComp property in the bb object, which is in return a function describing what to do when a history event occurs. To enable this function to listen to a history event you need to add the bb.document.addEventListener… section illustrated in the snippet. Inside the behavior the d:handler tag defines a mouse click handler, which adds the label and id of a clicked menu item as a string to the history stack. Now it should become clear what the body of the bookmarkComp function inside the resource tag is doing. It will just retrieve the id of a clicked menu item to the corresponding history event and performs an action on the menu item with that id. Here this means firing the click event, which results in loading the corresponding view.
The final step is adding the namespace uri and prefix to the application with the nsMap attribute on the bjsf:application and include the xml file containing the behavior.
<f:verbatim>
<xi:include href="behavior.xml" parse="xml" />
</f:verbatim>
Important
It is important to realize that this article describes a very specific scenario. The concept of history in an a realist application is mostly much more complicated. Best practice would be to define for example a general purpose history tag where all components, which need to be synchronized with the history object, subscribe to.
