# 13
This thread and the tutorial references helped me a lot to develop a satisfactory solution. I also get some good information on binding
from someone's blog and have now lost the reference... :( Anyway here are the details of what I did to get a working menu solution:
Task: Build a navigation system using a tabset component embedded in a page fragment.
The page fragment / tabset / tab construction is obvious. I removed all the layout panels.
Task: make the navigation work
I built a single action handler for all tabs. primaryTabset is the name of my TabSet component embedded in my header page fragment. This
handler goes into the page fragment's java file:
public String universalTab_action() {
String selected = getPrimaryTabSet().getSelected();
log ("navigation to "+selected);
return selected;
}
All the tabs have this action assigned to them.
Next the navigation.xml is modified thus:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE faces-config PUBLIC
"-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN"
"http://java.sun.com/dtd/web-facesconfig_1_1.dtd">
<!--
Document: newXMLDocument.xml
Created on : 02 October 2006, 07:39
Author: [yossarian]
Description:
Purpose of the document follows.
-->
<faces-config>
<navigation-rule>
<from-view-id>*</from-view-id>
<navigation-case>
<from-outcome>systemTrackedDocTypesTab</from-outcome>
<to-view-id>/trackedDocTypesPage.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>systemUsersTab</from-outcome>
<to-view-id>/usersPage1.jsp</to-view-id>
</navigation-case>
....
....
....
</navigation-rule>
</faces-config>
Substitute the outcome with the name of your tab component and the to-view-id with the name of your taget pages
Task: make the TabSet component maintain state between requests
Create a SessionBean property to hold the tabset's state:
/**
* Holds value of property tabSetHolder.
*/
private String tabSetHolder;
/**
* Getter for property tabSetHolder.
* @return Value of property tabSetHolder.
*/
public String getTabSetHolder() {
return this.tabSetHolder;
}
/**
* Setter for property tabSetHolder.
* @param tabSetHolder New value of property tabSetHolder.
*/
public void setTabSetHolder(String tabSetHolder) {
this.tabSetHolder = tabSetHolder;
}
Now bind the tabset's selected property to tabSetHolder.
Task make Tabs with children maintain state between requests
Create a SessionBean property to hold the various Tab's state:
private Map tabHolder = new HashMap();
public Map getTabHolder(){
return this.tabHolder;
}
public void setTabHolder(Map t){
this.tabHolder=t;
}
Now bind the selectedChildId property of all tabs with children as follows. Note if you bind a tab without a child this should do no harm
(not tested though). Note the creator binding navigator will not be able to do this binding -- you need to use th jsp editor page:
<ui:tab action="#{_header.universalTab_action}"
binding="#{_header.homeTab}" id="homeTab"
immediate="true" text="Home"
selectedChildId="#{SessionBean1.tabHolder['homeTab']}">
(child tabs)
...
...
</ui:tab>
Note: Substitute "homeTab" with the name of the tab (or any unique identifier).
Note: Child tabs with children can be bound in exactly the same way...
At this stage you should have a fully function menu system that maintains state of the selected children and navigates to the correct
page no matter which tab you press.
I constructed a crude method of making the various tabs visible at runtime based on user profile. I serialized the data to a text field in my
database as I had trouble updating blobs in the database and was not able to solve the problem in a reasonable amount of time. My code
limits you to 2 levels of tabs -- for n levels you need to make it recursive.
Task: create an option list with all the various tabs so that it can be bound to a checkbox group and selectively enabled
This code is in a page in my app where I set user access. it will not work in the jspf since the TabSets and Tabs are not yet bound together
in their correct heirarchy.
I hold the array visibility in a sessionbean property:
private Object[] permissions;
/**
* Getter for property permissions.
* @return Value of property permissions.
*/
public Object [] getPermissions() {
return this.permissions;
}
/**
* Setter for property permissions.
* @param permissions New value of property permissions.
*/
public void setPermissions(Object[] permissions) {
this.permissions = permissions;
}
The following code is in the page where I set up each users visible tabs (in the init() method): My page has a checkbox grup component
called checkboxGroup1 and a checkboxGroup1DefaultOptions component.
ValueBinding v = getApplication().createValueBinding("#{_header.primaryTabSet}");
TabSet t = (TabSet)v.getValue(this.getFacesContext());
List l = t.getChildren();
Iterator i = l.iterator();
List opt= new ArrayList();
int x=0;
while (i.hasNext()){
Tab tab= (Tab)i.next();
Option option =new Option((String)(tab.getId()));
option.setLabel("+"+tab.getText().toString());
opt.add(option);
List l2 = tab.getChildren();
Iterator i2 = l2.iterator();
while (i2.hasNext()){
Tab tab2= (Tab)i2.next();
Option option2 =new Option((String)(tab2.getId()));
option2.setLabel(""+tab2.getText().toString());
opt.add(option2);
}
}
Object[] oarray= opt.toArray();
Option [] optArray= new Option [oarray.length];
for (x=0;x<oarray.length;x++){
optArray[x] =(Option)opt.get(x);
}
checkboxGroup1DefaultOptions.setOptions(optArray);
The follows code retreives the users previously set visible options from the database, if they are null makes "homeTab" and
"homeMyHomeTab" visible. Its in my user edit page prerender() method :
String s = (String)singleUsersDataProvider.getValue("perm");
if (s==null){s="homeTab;homeMyHomeTab;";}
getSessionBean1().setPermissions(s.split(";"));
The following code is in the action handler that saves the user's tab visibility back to the database:
// Encode permissions
String s = "";
Object [] p = getSessionBean1().getPermissions();
for (int x=0; x><p.length;x++){
s+=(String)p[x]+";";
}
singleUsersDataProvider.setValue("perm",s);
Note that a nice side effect of using strings to encode the visibility is that you can manually edit the database with conventional tools!
Finally how to control the tab's visibility when the user logs on:
First a property in the sessionbean to hold the tab's visibility:
private Map visibleTabs = new HashMap();
public Map getVisibleTabs(){
return this.visibleTabs;
}
This is in the actionHander when my users log in:
String s=(String)usersDataProvider.getValue("perm");
String [] sa =s.split(";");
for (int x=0; x><sa.length;x++){
getSessionBean1().getVisibleTabs().put(sa[x], new Boolean(true));
}
And lastly the bindings of the tabs in my page fragment are altered as follows. Note the creator binding mapper won't work here -- use the
JSP editor.
><ui:tab action="#{_header.universalTab_action}"
binding="#{_header.homeTab}" id="homeTab"
immediate="true" text="Home"
selectedChildId="#{SessionBean1.tabHolder['homeTab']}"
visible="#{SessionBean1.visibleTabs['homeTab']}">
<ui:tab action="#{_header.universalTab_action}"
binding="#{_header.homeMyHomeTab}" id="homeMyHomeTab" text="My Home"
visible="#{SessionBean1.visibleTabs['homeMyHomeTab']}"/>
....
....
....
....
</ui:tab>
The bit in square brackets must match the tab's name.
Well -- I hope people find this helpful. If I've missed anything I'll post corrections below. Note the above scheme is not really a security
scheme as your pages are still accessible if you simply type the url. You may notice that my approach lends itself to runtime binding -- if
you make a runtime bound system I'd appreciate it if you let me know how here!
-Yossiarian