ColdFusion 8: AJAX UI Tabs

Yesterday I blogged about layouts in ColdFusion 8. This was accomplished with the handy cflayoutarea and cflayout tags. Today's post will use the same tabs but deal with tabs. (No, not bar tabs.) I'm a big fan of tabs for breaking up complex forms, so I'm happy to see this baked into the product.


Let's start with a simple example:

<cflayout type="tab">

   <cflayoutarea title="Tab 1">
   This is the first tab.
   </cflayoutarea>
   
   <cflayoutarea title="Tab 2">
   This is the second tab.
   </cflayoutarea>
   
</cflayout>

As you can see - this uses the same tags discussed in the previous post, except this time I used a type of tab. Insdie a tab based layout, all cflayoutarea groups will create a tab. The titles of the layoutareas will be the titles of the tabs. You can see a live demo of this here.

Building basic tab sets is trivial enough. ColdFusion provides many options for the tabs as well. As an example, you can supply a tab height to the layout control like so:

<cflayout type="tab" tabheight="100">

   <cflayoutarea title="Tab 1">
   <p>
   This is the first tab.
   </p>
   <p>
   This is the first tab.
   </p>
   <p>
   This is the first tab.
   </p>
   <p>
   This is the first tab.
   </p>
   
   </cflayoutarea>
   
   <cflayoutarea title="Tab 2">
   This is the second tab.
   </cflayoutarea>
   
</cflayout>

This will set the height for the tabs. The content in the tabs will automatically scroll if they need to. (If for some reason you don't want this, you can set the overflow attribute to hidden.) The code above can be seen here.

You can also set the tabs to display at the bottom:

<cflayout type="tab" tabheight="100" tabPosition="bottom">

Demo

By default, the first tab is selected, but you can also specify a default tab in the code:

<cflayoutarea title="Tab 2" selected="true">

Demo

You can even disable a tab if you want:

<cflayoutarea title="Tab 2" disabled="true">

Demo

Along with all of these options, there is a nice JavaScript API to work with the tabs. You can select a tab. You can enable or disable tabs. You can even hide or show a tab (although this wasn't working for me so I assume it is currently buggy). For an example of all of this (including the buggy show/hide code), see this final demo. The code for this demo is:

<cflayout type="tab" tabheight="100" name="mytabs">

   <cflayoutarea title="Tab 1" name="t1">
   <p>
   This is the first tab.
   </p>   
   
   <p>
   <form>
   <select onChange="if(this.selectedIndex != 0) ColdFusion.Layout.selectTab('t' + this.options[this.selectedIndex].value,'mytabs')">
   <option></option>
   <cfloop index="x" from="2" to="5">
   <cfoutput>
   <option value="#x#">Select tab #x#</option>
   </cfoutput>
   </cfloop>
   </select>
   </p>
   
   <p>
   <a href="javaScript:ColdFusion.Layout.showTab('hiddentab','mytabs')">Show Hidden Tab</a> /
   <a href="javaScript:ColdFusion.Layout.hideTab('hiddentab','mytabs')">Hide Hidden Tab</a>
   </p>
   </cflayoutarea>
   
   <cfloop index="x" from="2" to="5">
      <cflayoutarea title="Tab #x#" name="t#x#">
      <cfoutput>
      <p>
      This is tab number #x#.
      </p>
      </cfoutput>
      </cflayoutarea>
   </cfloop>
   
   <cflayoutarea title="Dharma Tab" name="hiddentab" inithide="true">
   This is the hidden tab. Can't touch this.
   </cflayoutarea>
      
</cflayout>

Comments

I get an error when I click on 'show hidden tab.'
# Posted By Gary Funk | 6/7/07 9:56 PM
Oh wow... this will be one of the first things i will be using when i get CF8. I have an application that has many pages of forms that would work perfect in a tabbed layout.

Now can each tab submit it's form content via Ajax to a CFC that inserts it into a database? Maybe on each tab change submit that tabs data?

I was thinking of converting the application i am talking about to Flex, but now i see this maybe i will keep it CF.
# Posted By Chad | 6/7/07 10:03 PM
Um, Gary, did you see where I mentioned this was buggy? :)
# Posted By Raymond Camden | 6/7/07 10:25 PM
Is there any provision for doing the tabs server side?

I have seen situations where each tab has a good amount of processing on them, and if the tabs are all loaded and changed client side, you can end up with ALOT of process to present the tabs.

Does <cflayout> have a runat="server" or something like that?
# Posted By David | 6/7/07 10:30 PM
The last demo is getting JavaScript errors in Firefox 2.0 when one click on the links on tab #1.
# Posted By Patrick Whittingham | 6/8/07 6:44 AM
Interesting...when the last demo errors on clicking the "show tab" link, the JavaScript debugger says the error is "_198.unhideTab is not a function" and provides a link to the cflayout.js file on the coldfusionjedi site...which I could then click on and open in a view source window (that's surprising and a bit disturbing).

I did a search in that source window for an unhideTab function, and yep, there's doesn't seem to be one, at least in that particular file.

Not the most readable JavaScript code I've ever seen, that's for sure.
# Posted By Brian Swartzfager | 6/8/07 7:11 AM
Brian - why is it disturbing that you could read the JS code? Thats normal. And the JS code is supposed to be optimized for speed, not readability (although I don't know if the JS code is completely optimized yet).
# Posted By Raymond Camden | 6/8/07 7:18 AM
Patrick: Yes - please see my blog entry where I note that the show/hide tab API seems currently broken. :)
# Posted By Raymond Camden | 6/8/07 7:19 AM
Does cflayout replace Spry TabbedPanels?
If so, how much of cf8 will be replacing various Spry tags?
# Posted By Phillip Senn | 6/8/07 7:25 AM
David - no - this is all ajax based code.
# Posted By Raymond Camden | 6/8/07 7:33 AM
Phillip - Yes and No. The beauty of these UI controls is that they make it bran dead simple. While there are plenty of options with them, if you want 100% control, you need to use your own controls. Spry would make that rather easy.

I plan on blogging about cfsprydataset later this month. (Too many darn features!)
# Posted By Raymond Camden | 6/8/07 7:34 AM
Ray, you're right: it's perfectly normal for the debugger to show the JS file. Don't know why it struck me as being odd: maybe the official Adobe statement at the top of it that threw me off a bit.

And yeah, I understand that optimization should trump readability, but readability is still nice to have. Not a criticism of CF8 (or Adobe, or apple pie, or whatever), just a personal preference.
# Posted By Brian Swartzfager | 6/8/07 7:38 AM
I disagree Brian. I think most frameworks ship 2 versions. A fat readable version and a skinny impossible to read one for production. The goal isn't to hide code by making it ugly, but to trim down the size as much as possible.
# Posted By Raymond Camden | 6/8/07 7:44 AM
Chad, 2 possible answers. First, AjaxLink lets you link and keep inside a layoutarea. Secondly, there is a ColdFusion.Ajax.submitForm JS function.
# Posted By Raymond Camden | 6/8/07 8:30 AM
Yes, it's brain-dead simple... and to be fair, you should probably mention that the first example also adds ten additional javascript and css files (about 325K) to the weight of your page. TANSTAAFL.
# Posted By Michael Long | 6/8/07 3:03 PM
And to be fair - this is still a RC. Adobe knows they need to do some scrunching on the JS files.
# Posted By Raymond Camden | 6/8/07 3:06 PM
True. But as a dedicated HTML/JS/CSS solution to this can be done in about 2K, that code is going to need a lot of scrunching.

Really, we're back to the old argument of heavyweight frameworks vs. lightwieght specialized code and which one is better suited to the problem at hand.

Either way, one should know the advantages--and disadvantages--of their choice.
# Posted By Michael Long | 6/8/07 3:30 PM
The error with the tabs is a simple fix.

And the problem is that Ray put the values around the wrong way, oh well.

Here is the new code that works.

<a href="javaScript:ColdFusion.Layout.showTab('mytabs','hiddentab')">Show Hidden Tab</a> /
<a href="javaScript:ColdFusion.Layout.hideTab('mytabs','hiddentab')">Hide Hidden Tab</a>
# Posted By Andrew Scott | 6/9/07 8:47 PM
Andrew, not quite sure if you meant anything by the "oh well", but I'm as perfect as the next person. ;)

So I took a look at the docs again, and it really seems unclear, but after rereading it twice - it does seem like the syntax is group/tab, not tab/group. I can file a doc bug to make this a bit clearer.
# Posted By Raymond Camden | 6/10/07 10:49 AM
No Ray, it wasn't a dig at you. More to the fact that I know that the docs could have been wrong.
# Posted By Andrew Scott | 6/11/07 12:46 AM
Bug in Safari - only the 5 tabs are displaying. The picklist is not showing up at all.
# Posted By Lola LB | 6/11/07 8:28 AM
I can confirm that. Please log a bug Lola.
# Posted By Raymond Camden | 6/11/07 8:42 AM
Done . . .
# Posted By Lola LB | 6/11/07 8:51 AM
Something I do not like about the height of the tabs.
The tab should be resizable as default giving chance to customize height and eventually scrollbar.
From my test the tab start with a default height ( looks like about 100px) and this reduce the possibility to be used without the orrible scrollbar.
If tab 1 has 5 lines of text and tab 2 25 lines I have 2 options:
1)Fix the height of the two tabs regarding the longest but tab 1 will be 85% white
2)let the tag add the scrollbars.

However there should be no way to be sure to avoid scollbars in dynamic context:

I 'd like to see tab resizable as default with no height limit ( as most tab scripts now in use) and attributes like tabheight with a reative scrollbar yes no.

Hope is all clear

Nowone noted the same situation or I miss something?

Andrea
# Posted By andrea | 6/12/07 8:06 PM
what if you want a multiple tab layout and a single form for all of the tabs? I have a cflayout type="tab" and four cflayoutareas, one for each tab. but I want it all wrapped up in a single form with a single submit button at the bottom. In flash forms, this is a no-brainer. Using Ajax UI I'm having trouble getting it to save the form data and I was wondering if there was a trick to getting it to work. I tried both ways I know, with a simple submit button and I tried a button with an onclick="ColdFusion.Ajax.submitForm..." command and nothin happens when I click. Also, I should say that the tab layout is pulled into a cfdiv tag from a ColdFusion.navigate link
# Posted By Michael White | 9/7/07 2:20 PM
Michael - I haven't tried that before. When I get time - I'll try to whip up a demo. Obviously the N step form is one of the intended uses for the tabs I think.
# Posted By Raymond Camden | 9/7/07 2:25 PM
I found a simplified example (sort of) here: http://www.mollerus.net/tom/blog/2007/06/tabbed_di...
I'm making progress, I think it's working now. Normal submit button and a cfunclude #Session.Source# on the bottom of the processing page
# Posted By Michael White | 9/7/07 2:41 PM
oh, and I mentioned in another post of yours that there are four "skins" for the tabs and windows stuff, default, gray, aero and vista. the may be in a separate set of directories but I am using something like this in the head section:    <LINK href="/CFIDE/scripts/ajax/ext/resources/css/tabsAero.css" rel="stylesheet" type="text/css">   

The tabsAero.css is just a copy of the tabs.css with the links to the image files changed.
it's not perfect but it's better than making a global change to all the tabs, windows.
# Posted By Michael White | 9/7/07 2:46 PM
Nice link.
# Posted By Raymond Camden | 9/7/07 3:56 PM
When I'm in a CFLAYOUT's tab, and submit form variables to an action page (outside of CFLAYOUT) which uses CFLOCATION to return user to the original tab (after some processing), the session and other variables are unavailable on the tab, and any subsequent activity on the tab acts very strangely.

I built a sample app to show the problems here.

TRY THIS WITH THE CODE BELOW:
start on Tab3. click Edit Mode, which correctly remains on tab 3. submit any text, which is submitted to the Action.cfm page, an you are CFLOCATION'd back to Tab3. However at this point in MY application (the real project his sample is debugging), all session variables would be unavailable to code processing on tab3 right now (variable not defined error). Regardless, NOW click Edit Mode again (the second time) and note how screwy things get. You leave the CFLAYOUT framework and go directly to the tab's page.

I think CFLAYOUT has a fundamental bug that manifests when users are CFLOCATION'd back to a tab after processing. Maybe the tab's page doesn't know to remind the browser that there is a parent CFLAYOUT template (but i thought that's what the CFFORM tag is doing!!). The only way I can think to fix this is to CFLOCATION back to the original page with the CFLAYOUT tag, and then (using URL params and CFIFs) decide which tab is "SELECTED". But when I do that, i get a Javascript error like: "Error processing JavaScript in markup for element cf_layoutarea1200957715744: ...."
Attach Code

<!--- cflayouttest.cfm --->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitiona...;
<html xmlns="http://www.w3.org/1999/xhtml">;
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>cflayout test</title>
</head>

<body>
<CFAJAXIMPORT TAGS="cflayout-tab,cfform">
<CFLAYOUT TYPE="TAB" TABHEIGHT="100%">

<CFLAYOUTAREA TITLE="TESTING Tab 1" SOURCE="cflayouttest-tab1.cfm"></CFLAYOUTAREA>
<CFLAYOUTAREA TITLE="TESTING Tab 2" SOURCE="cflayouttest-tab2.cfm"></CFLAYOUTAREA>
<CFLAYOUTAREA TITLE="TESTING Tab 3" SOURCE="cflayouttest-tab3.cfm"></CFLAYOUTAREA>

</CFLAYOUT>
<br />
<br />
<font size="-2">
Note:If you use CFAJAXIMPORT and use CFFORM for the form tags, then you don't need to SELECTED the CFLAYOUTAREA tags, since CFFORM does it for you.
</font>
</body>
</html>

<!--- cflayouttest-actions.cfm --->

<h1>CF Layout Test - actions</h1>

<CFSWITCH EXPRESSION="#FORM.EDIT#">

<CFCASE VALUE="SUBMIT-TAB1">

tab1

<CFDUMP VAR="#FORM#">

<CFLOCATION URL="cflayouttest-tab1.cfm">

</CFCASE>

<CFCASE VALUE="SUBMIT-TAB2">

tab2

<CFDUMP VAR="#FORM#">

<CFLOCATION URL="cflayouttest-tab2.cfm">

</CFCASE>

<CFCASE VALUE="SUBMIT-TAB3">

tab3

<CFDUMP VAR="#FORM#">

<CFLOCATION URL="cflayouttest-tab3.cfm">

</CFCASE>

</CFSWITCH>

<!---cflayouttest-tab1.cfm--->

CF Layout Test - tab 1 body<br />
<br />
<CFPARAM NAME="FORM.EDIT" DEFAULT="VIEW MODE">

<CFIF form.edit EQ "VIEW MODE">
VIEW-ONLY MODE<br /><CFFORM METHOD="POST" ACTION="cflayouttest-tab1.cfm"><input type="submit" name="EDIT" value="EDIT MODE" /></CFFORM>
<CFELSE>
EDIT MODE<br /><CFFORM METHOD="post" ACTION="cflayouttest-actions.cfm"><input type="submit" name="EDIT" value="SUBMIT-TAB1" /><input type="text" name="TESTBOX" /></CFFORM>
</CFIF>

<!---cflayouttest-tab2.cfm--->

CF Layout Test - tab 2 body<br />
<br />
<CFPARAM NAME="FORM.EDIT" DEFAULT="VIEW MODE">

<CFIF form.edit EQ "VIEW MODE">
VIEW-ONLY MODE<br /><CFFORM METHOD="POST" ACTION="cflayouttest-tab2.cfm"><input type="submit" name="EDIT" value="EDIT MODE" /></CFFORM>
<CFELSE>
EDIT MODE<br /><CFFORM METHOD="post" ACTION="cflayouttest-actions.cfm"><input type="submit" name="EDIT" value="SUBMIT-TAB2" /><input type="text" name="TESTBOX" /></CFFORM>
</CFIF>

<!---cflayouttest-tab3.cfm--->

CF Layout Test - tab 3 body<br />
<br />
<CFPARAM NAME="FORM.EDIT" DEFAULT="VIEW MODE">

<CFIF form.edit EQ "VIEW MODE">
VIEW-ONLY MODE<br /><CFFORM METHOD="POST" ACTION="cflayouttest-tab3.cfm"><input type="submit" name="EDIT" value="EDIT MODE" /></CFFORM>
<CFELSE>
EDIT MODE<br /><CFFORM METHOD="post" ACTION="cflayouttest-actions.cfm"><input type="submit" name="EDIT" value="SUBMIT-TAB3" /><input type="text" name="TESTBOX" /></CFFORM>
</CFIF>
# Posted By Jonathan | 1/30/08 3:00 PM
First off, please do not post such large quantities of code. I appreciate you sharing the code, but it is a bit much. ;)

When you use cfform inside cflayout, the form post will be asynchronous. Therefore, cflocation doesn't make sense. Your doing an Ajax based request and the result is a header saying 'go here', but that doesn't make sense in this context. What you need to do instead is something like this:

Use ColdFusion.Ajax.submitForm() to submit the form. This will let you capture the result and handle it. In that result, you can then do whatever. If you want the tab contents to change, you can ColdFusion.navigate.
# Posted By Raymond Camden | 1/30/08 3:09 PM
I was able to change the background color of the tabs using the post by James Mount. However, i was wondering if it is possible to change the color/text on a per tab basis? I have a set of tabs, and each of those tabs may have a set of sub tabs.

I was able to set the background color of the selected tab (both levels) to blue by modifying the style to:
<pre><p>##myTabs .x-tabs-strip .on .x-tabs-right .x-tabs-left {background: ##D6EEFA no-repeat right 0px;}</p></pre>

I was also able to set the text color of the sub tabs to red by modifying the style to:
<pre><p>##subTabs .x-tabs-strip .x-tabs-text {color:##ff8080;}</p></pre>

My question is, can i just alter the text/color of a specific subtab? I tried using the same syntax as above, but does this not work since it's at the &lt;cflayoutarea&gt; level? Can you only modify the tabs at the &lt;cflayout&gt; level?

The first sub tab is coded:
<pre><p>&lt;cflayoutarea title="Sub Tab 1" name="st1"&gt;</p></pre>

However the code below does not work...
<pre><p>##st1 .x-tabs-strip .x-tabs-text {color:##ff8080;}</p></pre>

Any help is greatly appreciated, thanks!
# Posted By Joe Finan | 4/30/08 11:57 AM
Thought I'd clean it up, sorry about the first post...

I was wondering if it is possible to change the color/text on a per tab basis? I have a set of tabs, and each of those tabs may have a set of sub tabs.

I was able to set the background color of the selected tab (both levels) to blue by modifying the style to:
##myTabs .x-tabs-strip .on .x-tabs-right .x-tabs-left {background: ##D6EEFA no-repeat right 0px;}

I was also able to set the text color of the sub tabs to red by modifying the style to:
##subTabs .x-tabs-strip .x-tabs-text {color:##ff8080;}

My question is, can i just alter the text/color of a specific subtab? I tried using the same syntax as above, but does this not work since it's at the <cflayoutarea> level? Can you only modify the tabs at the <cflayout> level?

The first sub tab is coded:
<cflayoutarea title="Sub Tab 1" name="st1">

However the code below does not work...
##st1 .x-tabs-strip .x-tabs-text {color:##ff8080;}

Any help is greatly appreciated, thanks!
# Posted By Joe Finan | 4/30/08 12:00 PM