Tab Sets and Page Fragments

I am working on a page fragment contaning a tab set and tabs for navigation purposes. While doing so I ran into two problems:

First I wanted to overrule some style rules; so I created my own .css file to overrule the CSS style classes for the Tab Set component in the defaulttheme.jar file. To my suprise I could not set the background color through the background-color selector, instead I had to use background-image selector with a url to an image being a .gif color swatch.

Second: I am having problems setting the currently selected tab, I did find some tips on this topic but I am working with JSC 2 update 1, and things seem to work different in this version.

Thanks for helping me out, in advance,

Annet.

[743 byte] By [JagX] at [2007-11-26 9:10:54]
# 1

> Second: I am having problems setting the currently

> selected tab, I did find some tips on this topic but

> I am working with JSC 2 update 1, and things seem to

> work different in this version.

What are the problems that you are running into?

To set the currently selected tab call tabSet1.setSelected("tab1") (replace tab1 with the id of the tab you want selected).

Here is a tip from from http://blogs.sun.com/roller/page/divas?entry=tabbing_thru_the_tulips

Look for this line:

<ui:tabSet binding="#{BannerPF.tabSet1}" id="tabSet1" selected="tab6" style="position: absolute; left: 0px; top: 0px">

Delete the selected="tabn" parameter. This is important. If you don't do that, then the current tab doesn't always highlight correctly because this JSP code is resetting the current selected value.

Important: Every time you edit the page fragment, you have to remember to remove the selected parameter from the JSP code. The IDE will keep sticking it in whenever you click on one of the tabs in the Visual Designer. So, if your tabs look like they aren't working, this is the first place to look (the selected parameter in the JSP).

jetsons at 2007-7-6 23:30:24 > top of Java-index,Development Tools,Java Tools...
# 2

Many thanks for your reply, Jetsons.

Here is a tip from: http://blogs.sun.com/roller/page/divas?entry=tabbing_thru_the_tulips

This tip was very useful.

You wrote:

To set the currently selected tab call tabSet1.setSelected("tab1") (replace tab1 with the id of the tab you want selected).

Where does this code go, I build te divas' BannerPF, they don't mention this piece of code, leave alone where to put it. Hope you can help me out again.

Kind regards,

Annet.

JagX at 2007-7-6 23:30:24 > top of Java-index,Development Tools,Java Tools...
# 3

I am sorry if you thought I was implying that the blog told you about setting the selected value. I did not mean to imply that. I was simply pasting an excerpt from the blog that explained a common problem, which may or may not have been your problem, because we have yet to discern what your problem is.

I cannot tell you when to call set selected because I do not know your program design.

Earlier I asked what problem you are having. Without knowing more specifics, it is really hard to provide useful information.

Depending on what you want to do, you would call setSelected in the prerender() or preprocess() method , or in an action, process value change or validation method.

You do not want to do it in an init method, as explained here: http://blogs.sun.com/roller/page/divas?entry=about_a_page_s_lifecycle

So, can you explain what you want to do and what problem you are having?

jetsons at 2007-7-6 23:30:24 > top of Java-index,Development Tools,Java Tools...
# 4

I've actually found that the tutorial does not work. If you select tab 'A' then 'Page2', then select tab 'B' and 'Page 4'. Then select tab "A', finally tab 'B'.

In the final selection, the 'Page 4' subtab is selected but page 3 is actually displayed. You do not see the page associated with the selected tab. I've added debugging code into the actions and the init() for the BannerPF jspf page but have not yet figured out how to ensure the correct tab displays the correct page.

At this point I abanonded using the page fragment approach for the Tabs however it's a curiosity so I work on it periodically when I have some time.

MWH@Keystroke at 2007-7-6 23:30:24 > top of Java-index,Development Tools,Java Tools...
# 5
I will take a look. Can you watch this thread so I can ask questions?Diva#2
jetsons at 2007-7-6 23:30:24 > top of Java-index,Development Tools,Java Tools...
# 6

Sure. I think it's really just a state management issue. For tab b they are; no child tab selected, page 3 selected, page 4 selected. The tab2_action() method only recognizes a single state 'page 3 selected'.So if you arrange for state 'page 4 selected', then navigate away from tab 'b' completely (by clicking on tab 'a'), then return to tab 'b' by clicking on 'b', the tab2_action() sets the navigation state to 'page3' but the tabset restores the tab state to 'page 4'. So you end up with page 3 being displayed but the 'page 4' tab selected.

MWH@Keystroke at 2007-7-6 23:30:24 > top of Java-index,Development Tools,Java Tools...
# 7
Thank you for pointing that out. What do you think of this solution?public String tab1_action() {tabSet1.setSelected("tab3");return "case1";}public String tab2_action() {tabSet1.setSelected("tab5");return "case3";}
jetsons at 2007-7-6 23:30:24 > top of Java-index,Development Tools,Java Tools...
# 8

Of course there is still the issue that if you select tab 'B', then 'Page 4' within tab B; then select tab 'A'; then return to tab 'B' by selecting 'B' you return to 'Page 3' not 'Page 4'.The developer must maintain the state of each level 1 tab, say via a session bean and restore the proper state when the level 1 tab is selected. When tab 'B' 'Page 4' is selected, set the value in the session. When tab 'B' is selected, retrieve the last level 2 sub tab selected and set it via the tabset1.setSelected('xxx') method. That way the system returns the user to the proper state relative to tab 'B'. Of course the same holds for tab 'A'.

The key is that developers need to understand they are explicitly responsible for keeping the tab state and navigation state in synch if they are using tabsets for navigation from within a page fragment. Which is quite different from using the tabset object from within a single page.

MWH@Keystroke at 2007-7-6 23:30:24 > top of Java-index,Development Tools,Java Tools...
# 9
May I quote you? With your permission I will add your statement to the blog and attribute it to you, MWH@Keystroke
jetsons at 2007-7-6 23:30:24 > top of Java-index,Development Tools,Java Tools...
# 10
Sure. No problem. It would also be good to get the 'tulips' tutorial updated. At least with respect to the caveats regarding state management. Would probably save some confusion.
MWH@Keystroke at 2007-7-6 23:30:24 > top of Java-index,Development Tools,Java Tools...
# 11

I will pass this thread onto the writer for the tab set tutorial writer, and I will add this to my "things to blog on" list. With jMaki and Jasper in my current queue and both the NetBeans pack and the next release of the IDE coming up, please don't look for it in the near future (but I will really try).

I appreciate your working with me and your important points on what needs to be addressed.

Chris

jetsons at 2007-7-6 23:30:24 > top of Java-index,Development Tools,Java Tools...
# 12
No problem. Glad to be of help. That's what 'community support' means.
MWH@Keystroke at 2007-7-6 23:30:24 > top of Java-index,Development Tools,Java Tools...
# 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

yossarian at 2007-7-6 23:30:24 > top of Java-index,Development Tools,Java Tools...
# 14

Info to add:

Sometimes you've navigated to a page using the main tabset and then performed some local navigation. For example the tabset got you to the master page of your database and you then click on an edit link to edit a record. You'd like to return to where ever the user was before they did the detail edit once they've saved or canceled their edit.

Here's how:

public String button1_action() {

// TODO: Process the button click action. Return value is a navigation

// case name where null will return to the same page.

return getValue("#{_header.primaryTabSet.selected}").toString();

}

The naming conventions are as described in my previous post.

I've got a page in my app that can be navigated to by multiple routes. This button action returns the user to where they expect.

yossarian at 2007-7-6 23:30:24 > top of Java-index,Development Tools,Java Tools...