Today's entry is cf_ext_navbar. It was submitted by Andrew Duval and reviewed by Todd Sharp. Todd's review is below in it's entirety with no editing from me. Thanks Andrew and Todd!

This contest entry is called CF_EXT_NAVBAR'and was created by Andrew Duvall and is an example of how to utilize the Ext3 JavaScript library included with ColdFusion 9. Many developers were thrilled when they learned that the Ext libraries that power the ColdFusion Ajax and UI widgets was being upgraded to the most recent version of the popular framework because there were a number of significant upgrades since the launch of ColdFusion 8 which bundled Ext 1.0. Andrew's entry demonstrates the required libraries and scripts to create a "navbar" widget.

When I first ran the entry I had the same reaction that I usually have when I see Ext widgets - "wow, that's pretty cool looking." Here's what the entry looks like:

My next step was to load up the code in ColdFusion Builder and once I got that done I loaded the entry up in the internal browser so I could have a peek at the code and the entry at the same time. Unfortunately what I saw in the internal browser wasn't so pretty:

Turns out my default browser in CF Builder was set to IE and since IE can't render HTML properly like every other browser on earth there was a bit of a problem. To be fair to Andrew, I'm sure this isn't his fault. What surprises me is that the Ext folks never caught this issue with the navbar in IE.

At this point I had the same second thought that I always do when I see Ext widgets - "I wonder how many scripts had to be imported just to create a simple navbar?" I opened up Firebug and took a look:

I was pretty shocked to see 590 KB of scripts and images for a simple navbar widget especially since my local environment uses gzip compression on scripts. The majority (519KB out of 590KB) of that bloat does come from a single file – 'ext-all-debug.js' which I'm pretty sure is the core Ext library (and is actually 2.5MB before being gzip'd). I swapped out the 'ext-all-debug.js' for the minified 'ext-all.js' and the total script size dropped down to 238 KB – much easier to swallow (but still pretty large for a simple navbar in my opinion). The fact that about 99% of the scripts and images are served from cache on subsequent page requests makes all of this a bit irrelevant if you were creating a full blown application but it's something to keep in mind if you're thinking of just dropping in a simple widget on a page somewhere.

So let's take a look at some of the code. I really would have liked if Andrew would have wrapped this up in a custom tag but since this is just a simple example I'll give him a pass on that. The first thing that caught my eye was the following – and this isn't necessarily related to ColdFusion 9, but it is a pet peeve of mine so I thought it was worth mentioning:

<cfif ListContainsNoCase('small,medium,large', scaleSize) EQ 0> <!--- alright, this is not a hacker challenge here; i expect only small, medium, or large to be passed in the URL for this demo ---> <cfset scaleSize = "large"> </cfif> <cfset scaleSize = lcase(scaleSize) /><!--- it is case sensitive inside the js --->

<cfscript> //NOTE: by changing [scaleSize] it retrieves the proper icons from the properly scaled icon folder //scaleSize = "large"; // values: small, medium, or large if (scaleSize EQ "small") { iconSize = "16"; // 1616 } else if (scaleSize EQ "medium") { iconSize = "24"; // 2424 } else { iconSize = "32"; // 32*32 } </cfscript>

So he starts out with a simple logic check that sets the 'scaleSize' variable as appropriate and then he does some simple clean up to make sure the variable is lower case. So far, so good, but then he immediately jumps into ablock to do some more conditional variable setting. He stays in cfscript for a little while but jumps back to using tags when he's generating some JavaScript later on. Personally I'm not a fan of cfscript, but I won't take anything away from those who are. My biggest issue is that jumping back and forth between tags and script creates inconsistent code that is difficult to read and follow. Now that CF9 has full cfscript support there is really no excuse to not pick one style and stick with it.

The next part I found a bit perplexing was the fact that Andrew created a 2d array of values in ColdFusion and then manually looped over the values to create the required JavaScript to create the navbar. Here's a snippet from his code that creates the 2d array:

<cfscript> stNavBar1.title[1] = "HOME"; stNavbar1.url[1] = "index.cfm"; stNavbar1.iconCls[1] = "icon-menu#iconSize#-1";

stNavBar1.title[2] = "SECOND BUTTON"; stNavbar1.url[2] = "index.cfm"; stNavbar1.iconCls[2] = "icon-menu#iconSize#-2";

stNavBar1.title[3] = "SCALE THE TOOLBAR"; stNavbar1.url[3] = "index.cfm"; stNavbar1.iconCls[3] = "icon-menu#iconSize#-3";

stNavBar2.title[3][1]= "Let's see a LARGE Toolbar"; stNavbar2.url[3][1] = "index.cfm?scaleSize=large"; stNavbar2.iconCls[3][1] = "icon-6-1";

stNavBar2.title[3][2]= "Let's see a MEDIUM Toolbar"; stNavbar2.url[3][2] = "index.cfm?scaleSize=medium"; stNavbar2.iconCls[3][2] = "icon-6-1";

stNavBar2.title[3][3]= "Let's see a SMALL Toolbar"; stNavbar2.url[3][3] = "index.cfm?scaleSize=small"; stNavbar2.iconCls[3][3] = "icon-6-1"; </cfscript>

And here is how he looped over it to create the JavaScript:

var mymenu=new SamplePanel({

tbar: [{ xtype:'buttongroup', hideBorders:'true', items: [ <cfloop index="i" from="1" to="#ArrayLen(stNavBar1.title)#"> <cfif i NEQ 1>,</cfif> { <cfif arrayLen(stNavBar2.title[i]) GT 0> xtype:'splitbutton', text: '<cfoutput>#stNavBar1.title[i]#</cfoutput>', iconCls: '<cfoutput>#stNavBar1.iconCls[i]#</cfoutput>', scale: '<cfoutput>#scaleSize#</cfoutput>', handler: navigate, url: '<cfoutput>#stNavBar1.url[i]#</cfoutput>', menu: [ <cfloop index="j" from="1" to="#ArrayLen(stNavBar2.title[i])#"> <cfif j NEQ 1>,</cfif> { text: "<cfoutput>#stNavBar2.title[i][j]#</cfoutput>", iconCls: "<cfoutput>#stNavBar2.iconCls[i][j]#</cfoutput>", tooltip:' <cfoutput>#stNavBar2.url[i][j]#</cfoutput>', url: '<cfoutput>#stNavBar2.url[i][j]#</cfoutput>', handler: navigate } </cfloop> ] <cfelse> text: '<cfoutput>#stNavBar1.title[i]#</cfoutput>', iconCls: '<cfoutput>#stNavBar1.iconCls[i]#</cfoutput>', scale: '<cfoutput>#scaleSize#</cfoutput>', handler: navigate, url: '<cfoutput>#stNavBar1.url[i]#</cfoutput>' </cfif> } </cfloop> ] }]

While that certainly works I tend to think he's overcomplicating things a bit. Take a look at a sample of the generated source code:

[ {

text: 'HOME', iconCls: 'icon-menu24-1', scale: 'medium', handler: navigate, url: 'index.cfm' } //additional objects as needed – possibly nested ]

So Ext is basically looking for a simple array of objects. To me it would have been much simpler (and easier to read) to just create an array of structs in CF and serialize it as JSON. Here's one way that might have looked. Remember that you'll need to use associative array, or 'bracket' notation to keep CF from changing the case of your struct keys when serializing.

<cfset menuArr = arrayNew(1) /> <cfset menuItem = structNew() /> <cfset menuItem["text"] = "TODD" /> <cfset menuItem["iconCls"] = "icon-menu24-1" /> <cfset menuItem["scale"] = "medium" /> <cfset menuItem["handler"] = "navigate" /> <cfset menuItem["url"] = "index.cfm" /> <cfset arrayAppend(menuArr, menuItem) /> <cfset jArr = serializeJSON(menuArr) />

Which produces the following JSON object:

[ { "scale":"medium", "iconCls":"icon-menu24-1", "text":"TODD", "handler":"navigate", "url":"index.cfm" } ]

The only issue with this version is that CF treats all the values as strings and if you notice Andrew's JSON object the value for the 'handler' key is actually a variable reference to the navigate JavaScript function. I worked around this by just replacing the quoted value "navigate" with an unquoted value in the JSON string that CF created:

<cfset jArr = replace(jArr,'"navigate"', "navigate", "all") />

Sure, it's a bit of a hack but I'm willing to accept a simple hack in exchange for not having my eyes bleed from trying to work with a 2d array.

Overall I think it was a good entry that took advantage of the Ext 3.0 features that ship with CF 9.

Download attached file.