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">
By default, the first tab is selected, but you can also specify a default tab in the code:
<cflayoutarea title="Tab 2" selected="true">
You can even disable a tab if you want:
<cflayoutarea title="Tab 2" disabled="true">
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
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.
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?
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.
If so, how much of cf8 will be replacing various Spry tags?
I plan on blogging about cfsprydataset later this month. (Too many darn features!)
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.
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.
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>
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.
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
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
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.
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>
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.
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 <cflayoutarea> level? Can you only modify the tabs at the <cflayout> level?
The first sub tab is coded:
<pre><p><cflayoutarea title="Sub Tab 1" name="st1"></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!
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!

