So I was using the cflayout tag to create a tab navigation system for reporting module. Each tab had a different report based on live data. The problem is that if you are on any tab that is not the default tab and you click refresh or press the F5 key to reload the data, then you get sent back to the default tab.

I needed to find a way to make it so that whenever a user clicked on a tab, their selected tab was saved in the session scope so that if they happen to use their browsers refresh links instead of using the refresh links I provided for them, they didn't loose which tab they were on.

To begin with set up a basic cflayout tab system like this:

<html>
<head>
<title>Tab Example</title>
</head>
<body>
<table width="95%" align="center"><tr><td>
<cflayout type="tab" name="MyTabLayout">
<cflayoutarea name="tab1" title="Tab 1" style="height:100%">
   This is Tab 1
</cflayoutarea>
<cflayoutarea name="tab2" title="Tab 2" style="height:100%">
   This is Tab 2
</cflayoutarea>
<cflayoutarea name="tab3" title="Tab 3" style="height:100%">
   This is Tab 3
</cflayoutarea>
<cflayoutarea name="tab4" title="Tab 4" style="height:100%">
   This is Tab 4
</cflayoutarea>
</cflayout>
</td></tr></table>
</body>
</html>

Try it here, you will see the 4 tabs and when you select tab 2, 3, or 4 then click your browsers refresh button (or F5) you get sent back to tab 1.

Now cflayoutarea has a selected attribute that takes a boolean value to flag a tab as being selected before you load the page. The problem is that there is no obvious way from the documentation to know how to save the selected tab to the session when a user selects it. To do this, we will need to make sure that session management is turned on in our application.cfc and we will need to set up a default for our session variable that will hold the active tab value. Here is my very basic application.cfc:

<cfcomponent displayname="Application" output="true" hint="Handle the application.">

   <!--- Set up the application. --->
   <cfset THIS.Name = "coldFusionSamples">
   <cfset THIS.ApplicationTimeout = CreateTimeSpan( 1, 0, 0, 0 )>
   <cfset THIS.SessionManagement = true>
   <cfset THIS.SetClientCookies = true>
   <cfset THIS.SetDomainCookies = false>
   <cfset THIS.ClientManagement = false>
   <cfset THIS.ScriptProtect = true>

   <cffunction name="OnSessionStart"
      hint="Fires when the session is first created."
      access="public"
      returntype="void"
      output="false">

      <cfset session.ActiveTab = "tab1">
      <cfreturn>
   </cffunction>
   
   <cffunction name="OnSessionEnd"
      hint="Fires when the session is terminated."
      access="public"
      returntype="void"
      output="false">

      
      <cfargument
         name="SessionScope"
         type="struct"
         required="true">

      
      <cfargument
         name="ApplicationScope"
         type="struct"
         required="false"
         default="#StructNew()#">

         
         <cfset structclear(sessionscope)>
      <cfreturn>
   </cffunction>
</cfcomponent>

Then we will need to set up a CFC to save the active tab name to the session scope, so I have created this basic CFC which can be called via AJAX to set and get session variables and named it session.cfc:

<cfcomponent name="session" hint="Performs Session Functions">
<cffunction name="SetSessionVar" access="remote" output="false" returntype="Boolean">
<cfargument name="Name" type="string" required="true">
      <cfargument name="Value" type="string" required="true">
            <cfset session[arguments.Name] = arguments.Value>
            <cfreturn true>
</cffunction>
   <cffunction name="GetSessionVar" access="remote" output="true" returntype="String">
<cfargument name="Name" type="string" required="true">
            <cfreturn session[arguments.Name]>
</cffunction>
</cfcomponent>

Next we need to set up a listener so that whenever a tab is clicked, it calls a javascript function to save the selected tab into the session scope.

<html>
<head>
<title>Save Active Tab Example</title>
<!--- cfajaxproxy is used here to create the js class for connecting
      to the session.cfc we created --->

<cfajaxproxy cfc="session" jsclassname="CFCs.session">


<script language="JavaScript">
//this is the function that adds the listener to
//the tabchange event in the tab object
addTabChangeListener = function(){
myTabs = ColdFusion.Layout.getTabLayout("MyTabLayout");
myTabs.on('tabchange',SaveActiveTab);
}
//this is the listener that will call the session.cfc
//everytime a tab is selected
SaveActiveTab = function(){
myTabs = ColdFusion.Layout.getTabLayout("MyTabLayout");
actTab = myTabs.getActiveTab();
var SessionObj = new CFCs.session();
      SessionObj.setErrorHandler(errorHandler);
      SessionObj.SetSessionVar("ActiveTab", actTab.id);
}
//this is an error handler used when calling the cfc
errorHandler = function(statusCode,statusMsg) {
   alert(statusCode+': '+statusMsg)
}
</script>

<!--- the AjaxOnLoad() function tells ColdFusion to
      Call the specified function once it is done loading
      all the AJAX objects on the page. I am using it here
      to call the function I made to create a listener on the Tabs
       --->

<cfset AjaxOnLoad("addTabChangeListener")>

</head>
<body>
<table width="95%" align="center"><tr><td>
<cflayout type="tab" name="MyTabLayout">
<!--- for each of the tabs I have added code to check the session scope and
   set the boolean value to be used for the selected attribute in the
   cflayoutarea tag --->

   <cfif session.activetab eq "tab1">
      <cfset isSelected="true">
   <cfelse>
      <cfset isSelected="false">
   </cfif>
   <cflayoutarea name="tab1" selected="#isSelected#" title="Tab 1" style="height:100%">
   This is Tab 1
   </cflayoutarea>

   <cfif session.activetab eq "tab2">
      <cfset isSelected="true">
   <cfelse>
      <cfset isSelected="false">
   </cfif>
   <cflayoutarea name="tab2" selected="#isSelected#" title="Tab 2" style="height:100%">
   This is Tab 2
   </cflayoutarea>
   
   <cfif session.activetab eq "tab3">
      <cfset isSelected="true">
   <cfelse>
      <cfset isSelected="false">
   </cfif>
   <cflayoutarea name="tab3" selected="#isSelected#" title="Tab 3" style="height:100%">
   This is Tab 3
   </cflayoutarea>
   
   <cfif session.activetab eq "tab4">
      <cfset isSelected="true">
   <cfelse>
      <cfset isSelected="false">
   </cfif>
   <cflayoutarea name="tab4" selected="#isSelected#" title="Tab 4" style="height:100%">
   This is Tab 4
   </cflayoutarea>
</cflayout>
</td></tr></table>
</body>
</html>

Try it out here and you will see that no matter what tab you are on, it will now stay on that tab if you refresh your browser.

-Happy Coding!

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
mccormick's Gravatar Scott,

Thanks for posting this. It's really useful!

e
# Posted By mccormick | 11/27/07 7:45 AM
Noel's Gravatar Hi Scott,

Great work!

If one was still stuck with application.cfm, how would you amend this to make it work.

Thanks,

Noel
# Posted By Noel | 11/30/07 1:13 AM
Scott Bennett's Gravatar @Noel

You would make sure that session variables are enabled in your cfapplication tag and add some code to set the default value of the session variable. Something like:

<cfapplication
   name="MyAppName"
   sessionManagement="true">

<cflock scope="SESSION" type="EXCLUSIVE" timeout="1">
<cfif not isdefined("session.ActiveTab")>
<cfset session.ActiveTab = "tab1">
</cfif>
</cflock>
# Posted By Scott Bennett | 11/30/07 1:26 AM
Noel Luneau's Gravatar Got it, thank you Scott for responding so quickly. I did a ton of reading on application.cfc last night.

I use a default.cfm master page with cfinclude and under this framework the tab state does not save. I've confirmed that the code is working by creating a page outside of this framework.

As an example I use http://applicationname/pages/default.cfm?Title=Man...

Do you have any suggestions?

Thanks again,

Noel
# Posted By Noel Luneau | 11/30/07 3:50 PM
Scott Bennett's Gravatar Noel,

It should still work fine with your framework. You just need to make sure that your CFC for saving the session variable is mapped correctly. Since your default.cfm file is including the Candidates/ShowCandidates.cfm file you will need to make sure that in ShowCandidates.cfm where you are doing the equivalent of:

<cfajaxproxy cfc="session" jsclassname="CFCs.session">

to set up the javascript proxy to the CFC that is saving the state to your session scope, you need specify a dot-delimited path to the CFC. The path can be an absolute filepath, or relative to location of the default.cfm file (not the ShowCandidates.cfm file).
# Posted By Scott Bennett | 11/30/07 5:44 PM
Charles's Gravatar Hi Scott

Great code; 99% works a treat for me.

But i`m struggling with the java script; i am using 3 layouts on 1 page so i need it to moniter 3 different sets of possible selected tabs.
I can get it all work apart from the javascript! for example i am using the tab names "options", "contacts" & "business".
Can you give me a hint what the javascript would look like if it were moniter more than one layout for tab changes at a time.
# Posted By Charles | 12/3/07 8:50 AM
Scott Bennett's Gravatar @Charles,

I started to write a comment to answer your question, but It was quite lengthy so I added a new blog entry instead.

http://www.coldfusionguy.com/ColdFusion/blog/index...
# Posted By Scott Bennett | 12/3/07 1:59 PM
Scott Bennett's Gravatar I have updated my Session.cfc that I used in this example so that the GetSessionVar function actually works, and I gave an example of how to invoke the function here:

http://www.coldfusionguy.com/ColdFusion/blog/index...
# Posted By Scott Bennett | 1/23/08 5:12 PM
Drew's Gravatar Your demo works great, but when I can't integrate it into my current cflayout.


addTabChangeListener is not defined

I loaded the demo on our servers and it works fine, the only differences I notice in the source is that AjaxOnLoad gets pushed up higher in the page on my website -not the demo-.

I assume that cfpod or cfwindow breaks the listener
# Posted By Drew | 4/4/08 12:14 PM
Drew's Gravatar Sorry for the above comment, I resolved my previous issue. The javascript literals used in your example must be inside a <head> tag.

Thanks for the insight, I had been looking all around for how to add listeners for cflayout tabbing. They seem to ignore this totally on the livedocs.
# Posted By Drew | 4/4/08 5:12 PM
Scott Bennett's Gravatar Yes, it's kind of picky that way. The JavaScript functions need to be defined in between the <head> and </head> tags on the page. If you look at the last box of code in my article, you will see that is that case in my example.
# Posted By Scott Bennett | 4/4/08 6:05 PM
Claude Petitpas's Gravatar Any reason why this would work fine in firefox but not in IE 6.0. I keep getting the error that activetab is undefined in session??? Sorry for posting so late on this.
# Posted By Claude Petitpas | 10/17/08 3:58 PM
SaravanaMuthu's Gravatar Thank u man. Finally i fixed my issues with this post.
# Posted By SaravanaMuthu | 1/2/09 8:20 AM
Jason's Gravatar I have a set of reports set up in a cflayout structure with about 5 tabs. For some reason the browser won't allow me to print the report as long as it's inside the cflayout tag. Is there some way for me to find out which tab is active at the moment and maybe use javascript to open that in a new window and send it to the printer?
# Posted By Jason | 2/11/09 3:58 PM
Scott Bennett's Gravatar @Jason,

You can use javascript to determine which tab is active by using the getActiveTab function like this:

AlertActiveTab = function(){
myTabs = ColdFusion.Layout.getTabLayout("MyTabLayout");
actTab = myTabs.getActiveTab();
alert(actTab.id);
}
# Posted By Scott Bennett | 2/11/09 4:46 PM
Jason's Gravatar Thanks Scott.
I'm gonna try and see if I can get the contents of the active tab to open in a new window so that I can get a full print out of the page.
The section below the scroll bar in the cflayout tab display does not normally get printed. The browser just prints the visible area of the page.
# Posted By Jason | 2/11/09 5:02 PM
Chad's Gravatar I've tried everything to make this work with no success. Is it possible that my IT dept. is blocking something here?
I have had no luck with the event listener actually updating the session.ActiveTab. I am using CF8.01 Enterprise Edition.

The code is correct and I tested it, but it simply does not remember the tab I selected after I refresh. Is this a CFAJAXPROXY bug for me?
I know you don't have much to go on here but I need some kind of answer here.

Thanks.
# Posted By Chad | 2/26/09 3:51 PM
Don's Gravatar This looks like an amazing amount of code to set a session variable. I haven't played with it yet, but I have this exact problem and was looking for solutions before diving in and recreating the wheeeel.

Anyway, why not just set a session variable equal to the tab when the tab is clicked on instead of going through the whole proxy, cfc route?
# Posted By Don | 9/1/09 9:45 PM
Scott Bennett's Gravatar @Don,

You can not just set a session variable equal to the tab when the tab is clicked on because the tabs switch on the client side (in the browser) without sending a request to the server. Since session variables are maintained on the server side, it is necessary to create a javascript listener that sends an AJAX request to your server when a tab is clicked.
# Posted By Scott Bennett | 9/1/09 11:12 PM
Bill's Gravatar Thanks Scott. Very helpful.
I have a issue though, not sure if you have an experience of handling it. I am trying to do paging in side each tab of cflayoutarea. I couldnot figure out how to maintain the tab in the main page.
I basically have three tabs each dispays reasult of a query base on the filter of a user form.
if I use Previous and Next by url parameter, I will simply navigate out of the main page, instead of doing pagination with in the tab. Do you have any advice as to how to tackle this situation. Appreciate your time and help.

<cflayout type="tab">
<cflayoutarea title="cat1" source="cat1.cfm" refreshonactivate="true" />
<cflayoutarea title="cat2" source="cat2.cfm" refreshonactivate="true" />
<cflayoutarea title="cat3" source="cat3.cfm" refreshonactivate="true" />
</cflayout>
# Posted By Bill | 2/24/10 12:42 PM
Scott Bennett's Gravatar @Bill
You can accomplish this by using the AJAXLink() function.
http://livedocs.adobe.com/coldfusion/8/htmldocs/fu...
# Posted By Scott Bennett | 2/24/10 5:38 PM
Bill's Gravatar Thanks Scott. That is a big help. I will check into it.
# Posted By Bill | 2/24/10 10:41 PM
Jeremy May's Gravatar Thanks a lot for this super juicy approach. This helped me tremendously with preserving selected values in select boxes! Cheers!
# Posted By Jeremy May | 2/17/11 5:58 PM
Prarthana's Gravatar Hi,
I am trying to save the active tab to session variable. However, my form is part of .cfm form , that gets included in a template.
So I don't actually have a <head></head> tag to work with, unless I add it to the template.
Is there a work around to execute javascript?
# Posted By Prarthana | 2/3/12 5:23 PM
Scott Bennett's Gravatar if you have code in an include file that you want to be included in the html header of the reponse, you could use the cfhtmlhead like this:

<cfsavecontent variable="HTMLHead">
<!--- put the HTML/JavaScript you need in the header here --->
</cfsavecontent>
<cfhtmlhead text = "#HTMLHead#">
# Posted By Scott Bennett | 2/3/12 5:39 PM
Prarthana's Gravatar Thanks.. will try out the changes on my form :)
# Posted By Prarthana | 2/3/12 6:22 PM
roger vega's Gravatar Hi Scott,

Awesome find! I've been struggling with the issue of not being able to
maintain the active tab state for awhile now. I have cfm pages that
have 2 tabs, when the user hits a submit button, the page goes
back to showing the 1st tab instead of the one that the user actually
hit the submit button. I'll give this a whirl and see if it works.

One question though: there is a bunch of whitespace that is being
loaded at the top of the content. I tried several measures but none
worked. Could this be due to hidden variables inside the cflayoutarea
generating whitespace?
# Posted By roger vega | 2/17/12 6:59 PM
Scott Bennett's Gravatar @Roger,

Are you referring to whitespace in the HTML source, or are you seing whitespace in the redering of the page in your browser?

The HTML source would be handled serverside with things like using <cfsetting enablecfoutputonly="true"></cfsetting>, or <cfsilent></cfsilent>, or <cfprocessingdirective supresswhitespace="true"></cfprocessingdirective> depending on the situation.

If you are just seeing some extra space when you view the page in the browser, you may have to add some CSS to your page to modify margins and stuff like that. A common thing that I find is form/cfform tags will add a bottom margin in some browsers, which can be fixed by setting form { margin: 0 } in your stylesheet.
# Posted By Scott Bennett | 2/17/12 7:14 PM
roger vega's Gravatar OK I tried this and it seems to work in FF but not in IE 7. As soon t
as the submit button is hit and the page is refreshed, it goes back
to the 1st tab when in fact the submit was done on the 2nd tab.
Also, in IE 7 the tab image is suddenly gone, disappeared. In FF
it shows fine.

I've got a lot going on in the include files. Could that be a reason
from this not working in IE? Is there someway I could check to
see what's causing the issue? Thanks
# Posted By roger vega | 2/18/12 4:47 PM
roger vega's Gravatar Pls. ignore my earlier comment - I got it working on IE7. It was
as Scott mentioned the order of including the js files. I've got a
shinola load of include files so I included these js files in the first
include's html head section.

Thanks again for sharing this Scott!
# Posted By roger vega | 2/18/12 9:34 PM
@Albert's Gravatar I have cfm pages that
have 3 tabs, the are contains links to reports.
when I go to tab2 and select link, it will open pdf report then
when I hit Back button it returne me to the tab1, and open tab2
when I refresh page.
# Posted By @Albert | 9/12/12 1:10 PM
 
Home | Blog | Contact | © 2001 - 2021 The ColdFusion Guy - Scott Bennett. All rights reserved.