Creating Tabbed Menus

Somebody asked on the forum how to create a Tab menu using standard JSF components.

Here's one way you can do it. It's not easy (yet!) but works. The solution involves the following steps:

  • Each tab will have its own unique page
  • The tab component itself is a page fragment. You include this fragment on each of the pages that belong in the tab arrangment.
  • The tab fragment is simply a grid panel laid out horizontally, with a link for each tab.
  • In the navigation file, you use wildcard rules to ensure that a hyperlink clicked in the tab fragment on any page goes to the corresponding tab page.
  • You use CSS to visually create the "tab" effects. To do this, you should have two styleclasses: one for the current tab and one for the non-current tabs. Use value binding to dynamically choose a styleclass for each cell based on whether this tab's page is selected.

First go ahead and create your TabFragment. In it, drop a gridpanel. Let's say you want 6 pages to be part of this tab. Set the columns property to 6 - this will ensure that the components are laid out in a single horizontal row.

Now drop 6 hyperlinks into the page. The tab text for the nested output text components should be the displayed tab names. The action properties for the action links should be logical navigation case names - these can be anything, as long as you use the same names in your navigation file. (Note that each link action you dropped results in two separate components - the link, which has an action property, and a nested output text, which has the text string. Use the application outline when in doubt.)

Next, go and create all six pages that will be part of the tabbed set. On each page, include the tab fragment. Next go to the navigation file, switch to the XML file view (because wildcards are not yet well supported in the design view), and add a rule like this - use cut & paste:

    <navigation-rule>
        <from-view-id>\*</from-view-id>
        <navigation-case>
            <from-outcome>viewPreferences</from-outcome>
            <to-view-id>/ViewPreferences.jsp</to-view-id>
        </navigation-case>
    </navigation-rule>

This assumes that one of the links in the tabbed fragment has the action attribute set to "viewPreferences", and when clicked, this will navigate to the ViewPreferences.jsp page.

Repeat the above for all the hyperlinks in your tabbed fragment such that each has a page, a corresponding link, and a corresponding navigation rule.

Voila! You're ready to run - you now have a functional (but ugly) tabbed window set up. You should be able to run and navigate the tabs as expected.

The next thing you'll want to do is create some sort of visual feedback for the tabs, such that exactly one tab is shown as "current" (e.g. it has a tabbed border). Likewise, you'll probably want to change the link styling for this tab (so it looks like plain text) and for the other tabs (so they look less like hyperlinks).

We can achieve this with CSS. Let's say you add these two rules to your stylesheet:

.selected { }
.notselected { }
We'll tweak these later. Now the big question is: How do we get the tab fragment to automatically style the different portions of the tabbed fragments according to which tab is selected?

Go to your tabbed fragment's backing bean, and add some code like this:

    // XXX You MUST keep this in sync with the actual links included in the page fragment,
    // and navigation destinations in the navigation.xml file!
    static String[] tabs = { "/Page1.jsp",
                             "/Page2.jsp",
                             "/Page3.jsp",
                             "/Page4.jsp",
                             "/Page5.jsp",
                             "/ViewPreferences.jsp"};

    private int getSelectedIndex() {
         FacesContext fc = getContext();
         String viewId = fc.getViewRoot().getViewId();
         for (int i = 0; i < tabs.length; i++) {
             if (viewId.equals(tabs[i])) {
                 return i;
             }
         }
         return-1;
    }

    public String getColClasses() {
        StringBuffer sb = new StringBuffer();
        int sel = getSelectedIndex();
        for (int i = 0; i < sel; i++) {
            sb.append("notselected,");
        }
        sb.append("selected,");
        for (int i = sel+1; i < tabs.length; i++) {
            sb.append("notselected,");
        }
        return sb.toString();
    }
The whole point of this code is to create a property in the page fragment, called "colClasses", which will return something like "notselected selected notselected notselected notselected notselected" if for example the second tab is selected. And how do we know which tab is selected? It's obviously the page that is including the tabbed fragment being rendered! So the getSelectedIndex() method goes out and finds out what the root page name is, then looks that up in its tabbed page list (which you need to edit obviously), and based on this computes a comma-separated list of style class names.

The list of style class names can be bound directly to the grid panel in the page fragment which is including the links. Go and select it, then set its columnClasses property to the value binding expression #{TabFragment.colClasses}. (You can do this by right clicking on the grid panel and choosing Property Bindings... too, then drill into your page fragment and locate the colClasses name. Don't forget to hit Apply.)

We're almost done. We now apply different styles to the different cells rendered for the tabs in the tab fragment. Now we just need to play with CSS to create tab-like effects.

Here are some things you'll want to try:

  • To create a "tab" visual effect, create a single line border on the left, top and right sides - but not on the bottom. You can use css like this for that:
          border-left: solid 1px gray
          border-right: solid 1px gray
          border-top: solid 1px gray
          border-bottom: none
    
  • Use different background colors to the selected and the unselected tabs:
        .selected {
          background-color: #cccccc;
        }
        .notselected {
          background-color: #999999;
        }
    
  • You'll probably want to turn off hyperlink colors and underlines, using "color: black; text-decoration: none" for example, and maybe enable hyperlink underlines when the mouse moves over a link, using a rule like "a[hover] { text-decoration: underline }
I wish I could share my stylesheet with you but I used an existing proprietary one. If anyone does work on this and wants to publish their solution feel free to append a comment! Good luck and let me know how it works out!

Comments:

In advance, sorry for my poor english... So, I tried to make a tabbed pane, and I succeed to obtain just the tab. But it isn't easy to obtain the entire tabbed pane because of the components kind and size whose are displayed in this. I tried to apply a Grid Panel with a border, but it's difficult to assembly the tab with the pane as it was a true tabbed pane... An idea?

Posted by Original Prankster on August 16, 2005 at 11:47 PM PDT #

I'm sorry, I don't really understand the question. But have you tried Creator 2 EA - it ships with a TabSet component which makes this all a lot easier?

Posted by Tor Norbye on August 16, 2005 at 11:54 PM PDT #

I cannot use SJSC2EA for my current project... He needs too memory, and early access should provide bugs. I don't know how expose my problem in english (can you read French? :D) Instead of having a squared panel with tab fragment, I just have tab fragment without square panel under tab fragment... Is that comprehensible? :) Thanks

Posted by Original Prankster on August 17, 2005 at 01:08 AM PDT #

I still don't follow, so describe it in French and I'll ask somebody for a translation.

Posted by Tor Norbye on August 17, 2005 at 01:36 AM PDT #

I am trying your solution. I have exactly same situation to work with. But I am facing a problem in including the tabFragment page in my pages. Does include works with JSF? I mean whether putting a static or dynamic include statement is suffice or do we need to add some code so that include would work? Also do we need to enclose the contents of tabFragments in a <f:subview .. tag? I would greatly appreciate if you provide the solution. -B

Posted by Bhushan on February 03, 2006 at 04:24 AM PST #

Post a Comment:
Comments are closed for this entry.
About

Tor Norbye

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today