NO MORE CAREER
POLITICIANS!
Get Out Of Our House: Replacing congress with TRUE citizens!
Contact Doug!
Learn About Doug!
View Doug Boude's online resume
updated 11/18/2009

View Doug Boude's profile on LinkedIn
Link to me!

Follow Doug Boude on Twitter
Follow me!

Be Doug's friend on Facebook
Befriend me!
(I promise not to follow you home)
OO Lexicon
Chat with Doug!
Recent Entries
You may also be interested in...
Web Hosting

best web hosting - top web hosting sites, thetop10bestwebhosting.com

Czech your Page Rank!
Check Page Rank of any web site pages instantly:
This free page rank checking tool is powered by Page Rank Checker service
Surf's Up!
Visit Egosurf.org and massage YOUR web ego!
My Score: 9,001
Doug's Books

Read (and recommend)

  • Men are from Mars, Women are from Venus
  • The Wisdom of Crowds: Why the Many Are Smarter Than the Few and How Collective Wisdom Shapes Business, Economies, Societies and Nations
  • Blink: The Power of Thinking Without Thinking
  • Head First Design Patterns
  • Transact-SQL Programming
  • What's So Amazing About Grace?
  • Just So Stories (Rudyard Kipling collection)

Reading

  • Prayer: Does it Make Any Difference?
  • Data Mining (Practical Machine Learning Tools and Techniques)
<< September, 2010 >>
SMTWTFS
1234
567891011
12131415161718
19202122232425
2627282930
Search Blog

Recent Comments
Re: Using Google as your CF Mail Server (by Mike at 9/07 4:02 PM)
Re: Viewing Option Text (in IE7) that's Wider than the Select List (by Nithin Chacko Ninan at 9/07 1:34 AM)
Re: Viewing Option Text (in IE7) that's Wider than the Select List (by Nithin Chacko Ninan at 9/07 1:33 AM)
Re: Configuring Apache To Use Multiple Versions of ColdFusion (by Lola LB at 9/06 6:28 AM)
Re: Configuring Apache To Use Multiple Versions of ColdFusion (by ComboFusion at 9/06 5:17 AM)
Re: Railo 3.1 on Windows Server 2008 and IIS7 - Part 3 of 3 (by Jon at 8/27 2:04 PM)
Re: Hosts File Changes Not Acknowledged on Vista 64 (by Spacy at 8/24 3:46 PM)
Re: THE DAY CFUNITED DIED (by ComboFusion at 8/23 10:50 AM)
Re: My Grandpa (by Tasha at 8/10 4:29 PM)
Re: Just What IS a 'Service Layer', Anyway? (by dougboude at 8/02 10:10 AM)
Categories
Archives
Photo Albums
Funnies (5)
Family (3)
RSS

Powered by
BlogCFM v1.11

16 March 2010
Leveraging Response Headers in Ajax Calls

If you're doing any sort of Ajax development, then you undoubtedly already are quite familiar with the different ways you can use an Ajax call. For instance, you can have the call return a fragment of fully formed html (such as a table populated with data). For a more advanced user, you may have your Ajax calls returning raw data in the form of JSON or XML and parse it on the client side. In either case, though, your single Ajax call is returning a single value. But what if you want several different discrete pieces of "stuff" returned in a single call? You could get creative, and do something such as this in your backend code:

<cfsetting enablecfoutputonly="true" />
<cfscript>
  stReturnData = structnew();
  stReturnData.htmlBlob = "<table><tr><td>bla bla bla</td></tr></table>";
  stReturnData.statistics = structnew();
  stReturnData.statistics.recordcount = 212;
  stReturnData.statistics.redWidgets = 32;
  stReturnData.statistics.blueWidgets = 54;
  stReturnData.someQuery = qryIRanPriorToThis;
 
  returnVal = serializeJSON(stReturnData);
</cfscript>

<cfoutput>#returnVal#</cfoutput>

The above would work perfectly fine. It would, however, require that you be willing to write some serious Javascript to access the individual pieces of data, and compels you to have to visualize how the data sets are structured and nested as a whole.

Again, nothing wrong with doing something like the above. But, when I find myself wanting to return more than one value with a single Ajax call, more often than not I will approach it by leveraging response headers.

Response headers are like the saddle bags on a Harley. If your Ajax response is the Harley, then the burley tatted dude (or dudette) riding it is your primary response value. This Harley, however, comes equipped with a pre-defined set of saddle bags that contain what I'm calling "peripheral" information, or metadata about the response, AND (this is most critical), the ability for you to add your own custom saddle bags before the response comes riding home to its caller!

I have never had a need to even look at or care about those pre-canned response headers. They contain name value pairs such as "Transfer-encoding:chunked", "content-type:text-html", "Date: Tue, 16 Mar 2010 15:43:35 GMT", and a couple of others. But, in order to satisfy needs such as I described above in the code sample, I DO add my own response headers and stuff them with values my Javascript needs.

Doing this in Coldfusion is very simple and easy (as are most tasks). Re-doing the example above, then, I'm going to choose one of the values to be my burley tatted rider (typically the value consuming the most bytes), and designate the rest to response headers, like so:

<cfsetting enablecfoutputonly="true" />

<cfset ReturnData = "<table><tr><td>bla bla bla</td></tr></table>" />

<cfheader name="recordcount" value="212" />

<cfheader name="redWidgets" value="32" />

<cfheader name="blueWidgets" value="54" />

<cfheader name="someQuery" value="#serializeJSON(qryIRanPriorToThis,true)#" />

<cfoutput>#ReturnData#</cfoutput>

So now my Harley has several new, custom saddle bags attached, each containing the value I set in the "value" attribute of the cfheader tag, and my main rider, or the actual response value is my ReturnData (the blob of html).

Let's look at the Javascript needed to utilize this response. In this example we are going to use the Prototype library to create a new Ajax Updater. Updaters simplify the task of making an Ajax call and stuffing the response value directly into a target element. So let's say we have a div with an ID 'dataDiv' that will hold a table of data. The Ajax updater line will look like this:

new Ajax.Updater('dataDiv',getDataURL,{parameters:params,method:'get');

 

 

Now, the line above deals exclusively with the response value and ignores anything to do with the response headers. In order to access those headers, we have to tell our Updater to pass its response (Harley, rider, saddlebags, and all) to a function that we designate. This is called "using a callback function", and our line of code will now look like this:

new Ajax.Updater('dataDiv',getDataURL,{parameters:params,method:'get',onComplete:processSaddlebags);

 

 

The Updater will update its target div with the response value, then hand the Harley off to the processSaddlebags function, which will look something like this:

function processSaddlebags(ajaxResponse){
  //let's set each of our response header values to a local variable...
  var recordcount = ajaxResponse.getHeader('recordcount');
  var redWidgets = ajaxResponse.getHeader('redWidgets');
  var blueWidgets = ajaxResponse.getHeader('blueWidgets');
  var objQuery = ajaxResponse.getHeader('someQuery');

  //now we can do 'stuff' with these values!
  alert('total number of records in our query: ' + objQuery['ROWCOUNT']);
  //etc....
}

It should be noted at this point that a most MAHVELOUS way for you to get a clearer picture of what is happening when your code makes Ajax calls is to use the Firefox browser with the Firebug plugin. By doing this simple thing, Firebug will provide you with all the details of every Ajax call you make. Here's a screenshot of a sample Ajax call and the kinds of info you get back (notice the custom header 'total' that I added, and the fact that I'm outputting that value in the html as the "Total Records" value):

response headers

screenshot of firebug ajax call showing response headers

response value

screenshot of firebug ajax call showing the response value

Now, all that having been said, there are size limitations to the usage of response headers that you should keep in mind. I could not find a specific reference that talked about the size limit of response headers, but I did do some experimentation to try and approximate what that limit is. I stuffed as many characters as I could into a response header, and the value was truncated at 6,653 characters (including spaces). I created two custom headers with the same 6,653 characters in it, and both were returned, so I'm assuming the limitation is per header (though common sense tells me there is probably a cumulative maximum size as well). Knowing that there IS a limit, my recommendation is that you use headers for smaller pieces of data and never to convey something as large as a JSON data set unless you know that it will be limited in size.

That's it boys and girls! Now go out there and leverage those response headers!

Posted by dougboude at 12:47 PM | PRINT THIS POST! | Link | 2 comments



19 February 2010
Registered Ajax Responders Not Responding Properly
Ajax Fundamentals
If you use the Prototype Javascript library and you chain your Ajax calls together at times, you may have run into the issue that I just dealt with regarding your registered responders not responding correctly! Since I spent more time than I care to share trying to figure out why my global responders weren't working like I thought they should, I thought I'd share what I learned. Hope it saves somebody some time! :)

Prelude

You may not be familiar with some of the terms I have used already, or some that I will use. If you feel you already have a good grasp on the lexicon surrounding Ajax and JS libraries, skip ahead to The Scenario; but if not, then allow me to clear up some of the fog before we press on. Please excuse me if I spend time on things that you may feel are "common knowledge", but in my experience there really is no such thing and I want to make sure every reader is thinking in the same context.

The Basic Nutshell of Ajax Chaining

In Prototype and other similar libraries, an Ajax call is typically of one of two flavors: simple request, and an update request.

Simple Requests

A simple request is one in which you ask your JS to make an http call to the server and hand you back the results that were delivered, whatever they be. At that point you will have written more Javascript to actually deal with those results manually. For example, let's say you display a select list containing possible food categories to your user. When the user selects a category, an Ajax request is made passing in the selected category, and a JSON representation of a record set containing foods in that category is returned. That JSON object is handed off to another function you wrote which will 'walk' through the foods and populate yet another select list with the specific items.

Update Requests

An update request is one in which you are asking your JS to make an http call to the server and stuff the results, whatever they be, directly into a DOM element (a DIV tag, a SPAN tag, etc.). Let's make a slight change to the example cited above. In this scenario, we already have a DIV tag in our html that we have designated as the place where our select list should appear. So, instead of having our server code return a raw JSON representation of the data, we write our server code to produce the HTML for the fully populated select list ("<select name='selFoods'><option value='1'>Crescent Rolls</option><option value='2'>Dog Biscuits</option>....</select>"). It is this string that our Ajax call receives, and this returned value is stuffed directly in to the DIV tag we designated as the recipient. Make sense?

Registering Responders

It is quite typical (and useful) to show to your users some kind of "working" image to indicate that the Ajax call is in progress. We may also wish to disable certain items on the user interface while calls are in progress, or overlay an opaque div to prevent the user from clicking anything. The thing we want to do and/or display while an Ajax call is in progress is what we refer to as our "Responder". Within our Javascript, we will tell our library something along the lines of "hey, anytime an Ajax call is created (started), I want you to do this; and anytime you see an Ajax call finish, I want you to do this". The act of telling our library what we want to happen automatically regardless of the Ajax call is called "registering responders". You define (register) them one time, and they just work. :)

Why Would I Ever Want to Chain My Calls?

I'm glad I asked! Let me just give you a working example to help illustrate why we might do this on occasion, and how we do it.
In my application, I am allowing a user to upload a data file. When the upload of the file is complete, I then need to kick off a series of things to occur, and I want to update a DIV with the current status of each call as they happen. The first call is to validate the uploaded file, so I write an Ajax simple request that tells the server where the file was uploaded and what kind of file it is supposed to be. This call will either return a true or false, depending on whether the file passed or not. If it failed, I want to tell the user that the file was bogus and cease any further processing. If it passed, I want to kick off another Ajax simple request that tells my app to go ahead and proceed with step 1 of the file processing. That call will return success or failure along with status messages, and then will kick off the third step in the processing.

Now, the way we "connect", or "chain" these calls together is by taking advantage of the fact that our library knows when a call is finished. When we write the JS for call 1, we tell it "when you receive a response from the server, pass that response on to Call 2 for evaluation". Call 2 is written to check the success status, and if all is well will make an Ajax simple request which itself has been told to pass its results on to Call 3. Here's an abbreviated example of what I'm talking about (some code ommitted for brevity):

function Call1(){
   new Ajax.Request(validateUploadURL,{parameters:params,method:'post',onSuccess:Call2});
}

function Call2(response){
   var objRetval = response.responseJSON;
   if(!objRetval.success){
        $('status').update(objRetval.msg);
        return;
   } else {//call successful! carry on
     new Ajax.Request(fileProcess1URL,{parameters:params,method:'post',onSuccess:Call3});
   }
  
}

function Call3(response){
   var objRetval = response.responseJSON;
   if(!objRetval.success){
        $('status').update(objRetval.msg);
        return;
   } else {//call successful! carry on
     new Ajax.Request(fileProcess2URL,{parameters:params,method:'post',onSuccess:TheEnd});
   }
  
}

function TheEnd(response){
  var objRetval = response.responseJSON;
  $('status').update(objRetval.msg);
}

See how they're all connected? Chain Chain Chaaaaaaainnnnnn... yeah.

The Scenario

In my scenario, I have a file upload process that is composed of three different Ajax requests, all chained together in a conditional, synchronous fashion (I know there are other ways to approach it, but this is the one that fit my needs the best). I have registered global responders to show/hide an animated spinner to indicate when a request has started and finished (I 'show' it when the request starts, I 'hide' it when its finished). Now, in my mind, these global responders should toggle that gif every time a request starts or stops, so in this scenario, it should show/hide three different times. But, that isn't how it worked. The gif would show during the initial call, and hide after that call was finished; Then it never showed again, despite the fact that there were multiple additional calls made! Here is how I registered my responders:

    Ajax.Responders.register({
          onCreate: function() {
            showUploadWorking();
          },
          onComplete: function() {
            hideUploadWorking();
          }
        });


After much experimentation, it occurred to me that perhaps even though the responder was global, maybe I needed to be concerned with manually 'watching' the active request count variable. I don't understand why I SHOULD have to, but that did turn out to be the solution. Here is the properly working responder registrations:

    Ajax.Responders.register({
          onCreate: function() {
            showUploadWorking();
          },
          onComplete: function() {
              if(Ajax.activeRequestCount == 0){
                  hideUploadWorking();
              }
          }
        });

Notice the only difference is in the onComplete function...I am 'watching' the activeRequestCount variable (which as you may have guessed is simply a counter telling us how many requests are currently not yet finished) and conditionally hiding my responder.

Doug out :0)
Posted by dougboude at 11:20 AM | PRINT THIS POST! | Link | 1 comment
26 April 2009
Auto-Escaping Characters When Outputting JS Function Calls

I'm blogging this little snippet mostly so that I have a place to find it the next time I need it, but perhaps it'll come in handy for someone else as well.

I'm creating some Javascript function calls on the fly as I output some query results. One of the parameters in the JS function is the value of an item's title which may at times contain characters JS tends to barf on, such as the single quote: '

In order to automatically escape such characters as I create my JS call, I used CF's ReReplace function. This function utilized a regular expression in order to do a search and replace, so I just created a simple regex that contained a list of all the characters i wanted to be automatically escaped.

Snippet time.

The specific code that performs the replacement:

ReReplace(Ucase(videotitle),"(['|.|;|?])","\\\1","all")

Distilled version of the output code:

<cfoutput query="qryVideos">

<a href="##" onclick="playVideo('#videoID#','#ReReplace(Ucase(videotitle),"(['|.|;|?])","\\\1","all")#','#vidPathRoot#/#videoPath#');return false;">watch this video</a>

<br>

</cfoutput>

Posted by dougboude at 6:49 PM | PRINT THIS POST! | Link | 4 comments
22 January 2009
Special Character/Unicode Issue in Ajax Data Retrieval

My most recent project has caused me to have to be "unicode aware" at times (something I've never had to do before), and so I am learning a lot about encoding and display of special characters as I go along. My latest challenge related to this topic involved a User Manager section I created, wherein the users could very well have names that contain special characters (foreign names). This particular section performs its updates, deletes, and inserts via Ajax calls and client-side JS manipulation of a JSON data set. My Ajax is performed via the Prototype library, my code is all ColdFusion living within the Coldbox framework, I'm using Coldspring to manage my object relationships, Transfer is my ORM, and my backend database is MSSQL 2005.

The Challenge: Data that contained special characters was being successfully inserted/updated via my Ajax calls, but the JSON data set returned via those calls did NOT contain those special characters (or contained an incorrect interpretation of them, like numbers, question marks, etc.). A quick check of the database verified that the data was indeed stored in the tables properly.

Setting the Stage
At this stage in the game for me, the smorgasbord of terms, acronyms, and concepts revolving around properly handling unicode is a bit foggy for me. (On a side note, I WISH someone who has the full understanding would put together a simple "checklist" of "Things you need to do in order to handle special characters in ColdFusion"!) From what I currently understand, the physical template you write has to be "encoded" properly (set within the IDE you are using); The database you are using has to have the proper encoding(called Collation in MSSQL 2005); The fields in your table have to be of the proper type to store unicode text(ie: 'nVarchar' instead of 'Varchar', etc.); your browser has to have the proper languages associated in order to display certain sets of special characters(Tools/Internet Options/Languages in IE); your JS functions, if living in a separate file, must have the page encoded properly(again, via your IDE); your ColdFusion datasource has to have the checkbox for "Enable High ASCII characters and Unicode for data sources configured for non-Latin characters " checked; and to top it all off, after having handled all of that, your JS functionality still yet needs to have ITS encoding types set in the proper place.

That sounds like a LOT of fiery hoops just to be able to deal with special characters, right? Well I'm with ya...it's on the verge of being a nightmare for someone who's never had to deal with it. And I do realize that for some of you reading this, the first time YOU tried to deal with special characters, everything just frickin "worked right out of the box" and you probably didn't have to do but one or two of those things, at the most. I say that you got lucky that things were configured just so for the particular character set you were dealing with, and even though from your perspective it didn't seem like that big of a deal, the fact is having an understanding of what's going on behind the scenes can be pretty doggone important anyway, just in case you suddenly get the directive to start storing characters from some other encoding scheme that you AREN'T prepared for out of the box.

Okay, so back to my challenge. Here's the nutshell of how my process flows:

My initial page load is provided with a query of all of the users in the system. That query is then translated to a JS object using a line in my template like the following:

<script>
 //make our initial data set available to JS...
 var objUsers = <cfoutput>#serializeJSON(qryUsers,true)#</cfoutput>;
....

When a user is chosen for edit, I load up the values for that user from the client-side data set into form fields, allow them to be edited, then submit the form values back to Coldbox via an Ajax call where the record gets updated. After the update occurs, my event grabs a fresh copy of the user query (which now contains the updated record), serializes it, and returns it as a JSON string to the Ajax call. Here's the line of JS that performs the Ajax call:

new Ajax.Request(saveURL,{parameters: myparams, method:'post',onCreate:showWorking,onComplete:postSave});

Here is the line in my handler(controller) that returns the data to the call:

<!--- grab a fresh copy of the users to pass back as json to the call, sans a view --->
<cfset arguments.event.renderData(type="plain",data=serializeJSON(variables.userService.getAllUsers(activeOnly=false),true),contentType="application/json) />

 

Bear in mind that the "getAllUsers" method call you see is the exact same method call being used during the initial page load to retrieve the data, which DOES contain the special characters as it should.

So here is where the problem manifests itself. The JSON string that the "postSave" method is provided with has the special characters stripped out! Poof, they are just gone. Okay, so let me go and investigate some of the optional parameters that Prototype provides for its Ajax.Request method and see if any of them might apply in this situation....  Ah, here are a few! 'encoding', 'evalJSON', 'sanitizeJSON'. Well, playing with all three of these resulted in zero changes to the symptoms. Sheesh, I've encoded everything I can possibly think to encode...what else is there? After a lot of google time, skimming page after page of semi-related (but not directly relevant) info, I came across a tiny little tweek to the contentType being returned that I tried, and lo and behold it frickin worked! Here is the new line that returns a CORRECT data set to my Ajax call:

<cfset arguments.event.renderData(type="plain",data=serializeJSON(variables.userService.getAllUsers(activeOnly=false),true),contentType="application/json; charset=UTF-8") />

The difference: adding in "charset=UTF-8" to the contentType of the data being returned. Apparently THAT'S what JS was looking for all along.

I hope this helps someone else avoid a huge loss of time. And again, for those of you out there who know this stuff inside and out and can actually visualize how it all works in your head, it sure would be an assett to the community if you could put that info into a kind of "checklist" a person could use to make sure they have all of their Unicode ducks in a row when trying to deal with special characters! Pretty please?

Doug out.

Posted by dougboude at 10:54 PM | PRINT THIS POST! | Link | 6 comments
10 December 2008
Element.show/hide anomoly in Prototype

I am a lover of the Prototype Javascript framework, I must say. But today I found a rather irritating little tidbit that diverted my attention from "real" work. Thought I'd share it just in case it saves someone else a little hair pulling.

The Scenario: You have a button that, onClick, performs an Ajax.Updater call. For aesthetic reasons, you have a hidden div with a spinner gif in it that you unhide while the call is working, then when complete, you hide it again. A common practice, right?

<div id="working" style="display:none;"><img src="images/spinner.gif" /></div>

Well, when I initially laid out my page I just put my css styles inline until I got them the way I wanted. Afterwards I moved the styles out to an external file. That's when I noticed that my "working" div was no longer showing during the ajax call. The call was taking place, the JS function that performed the call hadn't been touched, yet no spinner. Here's my Ajax call:

new Ajax.Updater('targetDiv','index.cfm?param1=bla),{
 method:'post',
 onCreate:function(){
  Element.hide('container1');
  Element.show('working');},
 onComplete:function(){
  Element.hide('working');
  Element.show('container1');
 }
}
);


Having nothing else to try, I removed the style from my external file ( #working{display:none;} ) and put it back inline with my div tag (<div id="working" style="display:none;">...) and voila! I get my spinner again. Ah, one more thing to try...let me put the style back in the external sheet and then modify my onCreate callback function a little:

onCreate:function(){
Element.hide('container1');
$('working').style.display = 'inline';}

Manipulating the display setting more directly, NOW I get my spinner again. Sheesh, so what's the Element.show method doing, anyway? I dig through prototype.js and find that, while the 'hide' method is setting the element's display property to 'none',  the 'show' method is simply setting the display property to ''; nothing, empty string. Which works fine as long as my style is defined inline, but moving to an external style sheet breaks it. I observed this behavior in both IE and Firefox, current versions, so I don't believe it's a browser compatibility issue.

 

The short quick answer is, you have three options for being able to properly show/hide elements using Prototype:


1. keep the display style inline for elements you want to show/hide';
2. move your style external, then use the more direct method of swapping display styles ($('mydiv').style.display = 'inline');
3. move your style external, omitting the 'display:none' item, and use Prototype itself to hide the element on page load, like so:

<script>
 Element.observe(window,'load',function(){Element.hide('loginWorking');});
</script>

If you let Prototype do the hiding, it shows and hides just fine.

 

Thoughts?

Posted by dougboude at 2:49 PM | PRINT THIS POST! | Link | 1 comment
18 November 2008
TinyMCE Refusing to Display Icons
or, A Descent into Madness

Okay, this post is as much informative as it is a rant, so bear with me while I vent as I share.

Let's talk TinyMCE. Typically I use FCKEditor, but for my current project I'm going with TinyMCE. How hard can it be, right? An editor is an editor is an editor. I download it, I add the two simple lines needed to transform my textarea into a full blown editor, and voila: it's an editor. But the menu has NO icons to be seen! First impulse is that I obviously have a bad path for the images. But no, TinyMCE uses "embedded sprites" or something like that to display its icons, so it isn't a pathing issue. If I right click where an icon should be, I see it for a moment, but then it's gone again.

The symptoms

tinyMCE not showing its icons

What it should look like

tinymce editor with icons displayed

So I wade through dozens of google results that look like they *might* provide a clue. One is a thread on the TinyMCE forum where the symptoms are the same as mine. I read through the replies looking for that silver bullet, but the only clue I find is that the user eventually solved their problem by removing the div tag from around the textarea. Not a solution for me, because I kinda NEED my divs to provide structure to my layout (I thought we all did), but it does give me reason to believe that perhaps I have implemented some css that TinyMCE doesn't like. So I move my textarea to other parts of the layout and reload until I finally get the icons to show up. Aha! I've narrowed it down to the div area that causes the symptoms, <div id="inner">! So let's see what horrific css i've applied to that div that broke TinyMCE.

#inner {display:block;margin-left:-200px;margin-right:-210px;padding:5px;}

Well, it doesn't LOOk so horrible at first glance. Let me give that div a new ID and add a new line to my css, adding style elements until I see it break again. So now in my layout I have
<div id="innerr">
and in my css I have
#innerr {display:block;}

Reload. Okay, I see my icons. Add another element:
#innerr {display:block;margin-left:-200px;}

Reload. still see my icons. Add another:
#innerr {display:block;margin-left:-200px;margin-right:-210px;}

Reload. Wow, still see my icons. Surely it can't be the padding that's killing it! Let me add it:
#innerr {display:block;margin-left:-200px;margin-right:-210px;padding:5px;}

Reload.
I STILL see all the icons. I have just completely duplicated the style that was applied to div "inner", where my TinyMCE icons would NOT show themselves, and yet I now DO see the icons. Wait, let me try one more thing...

<div id="inner"> (rename the id back to the original value)

I rename my div ID back to "inner". It has the same exact style as "innerr". But my icons disappear again. WHAT THE HECK?

Obviously ID "inner" is reserved for TinyMCE in some way. Perhaps that was actually mentioned somewhere in the docs, but I didn't see it. So anyway, if you are using TinyMCE and experience surreal symptoms such as disappearing icons, step 1: change your div ID's and class names and see if the symptoms go away. It's likely some style or name you've used that stepped on TinyMCE's tiny little toes.

Posted by dougboude at 1:17 PM | PRINT THIS POST! | Link | 2 comments
04 November 2008
Elegant Approach to Disabling Submit Button on Forms

A while back I had a project that required the submit button of the login form to be disabled unless both the username field AND the password field had values. There are probably several ways to skin that cat, but my good friend Boyan Kostadinov offered a more elegant solution (he's always good for the most elegant approach!), and I thought I'd share it with whoever else may benefit.

This solution uses the Prototype library (you need version 1.6). It also uses a small "login.js" file that Boyan wrote which looks for anything with the class "enableOnEntry" (the key to making this work) and then cycles through the form elements within that class, adding their IDs to an array (actually a delimited list, but it is split into an array later). Boyan then binds event listeners to each of the form fields which, while typing in values, will check to see if the submit button should be enabled or not. Anyway, you can read through the JS file if you like, but here's how to implement it, along with a working demo in this post:

1. acquire and reference the following js files in your template (in this order):
    prototype.js
    login.js
2. ensure that your form tag has the class "enableOnEntry";
3. ensure that your form has exactly one submit button (of type 'submit', not 'button');

That's it!

Working Demo:

For your convenience, here is the code used to create the working sample above:

<script src="/js/prototype.js"></script>
<script src="/js/login.js"></script>
<fieldset style="width:200px;">
<legend>Login Form</legend>
<form class="enableOnEntry" name="login" id="login" onSubmit="alert('form submitting...');return false;">
 Username:<br><input type="text" id="username" name="username" value="" /><br><br>
 Password:<br><input type="password" id="password" name="password" value="" /><br>
 <br>
 <input name="submit" id="submit" type="submit" disabled="true" value="Login" />&nbsp;&nbsp;<input type="reset" value="Clear" onClick="$('submit').disabled = true;" />
</form>
</fieldset> 

 and here are links to the js files:

 

 prototype.js

login.js

Hope it helps.

Posted by dougboude at 5:55 PM | PRINT THIS POST! | Link | 3 comments
14 May 2008
Viewing Option Text (in IE7) that's Wider than the Select List
Though a "minor" cosmetic issue at times, it can be challenging to come up with creative ways to accommodate what I consider to be IE's shortcomings regarding the control of form items, in particular select lists. With at least one project I'm currently working on, I have a select list that lives in a fixed width div, yet there are times when the text values of the options are wider than the div itself. The client respectfully requested that I find a way to make the full option text display whenever the user clicks on the select list, so I of course referred to my reliable friend Google. After investing a couple of hours exploring different approaches to the challenge, I found none that I could get to meet my needs in both Firefox and IE. Oh, by the way, Firefox AUTOMATICALLY displays the full option text when you drop down without any developer intervention needed.  Do you realize how much development time could be saved if IE worked the same as Firefox??? Anyway, I digress.

The solution I came up with (which I didn't find anywhere in all of my googling) was very simple: when a user clicks the select list, swap out the class to one without width restrictions. When they make a selection, swap the class back to one WITH width restrictions.

I make sure that I set my container div to a fixed width and set the overflow to 'hidden', that way when my select list suddenly grows, it doesn't force the div to widen OR overlap any adjacent divs. Without further adieux then, a simple before and after (bear in mind that if you're viewing this in Firefox, you won't see any problem with the first list; it's only BILL'S BROWSER that has the issue):

I am the problem select list:




I am the better select list:



Here is the code for the samples above:

<style>
.ctrDropDown{
    width:145px;
    font-size:11px;
}
.ctrDropDownClick{
    font-size:11px;

    width:300px;

}
.plainDropDown{
    width:145px;
    font-size:11px;
}
</style>
<div style="padding:4px;width:175px;height:75px;border:thin solid black;overflow:hidden;">
I am the problem select list:<br><br>
<select name="someDropDown" id="someDropDown" class="plainDropDown">
    <option value="">This is option 1.</option>
    <option value="">This is the second option, a bit longer.</option>
    <option value="">Drink to me only with thine eyes, and I will pledge with mine...</option>
</select>
</div>
<br>
<hr width="150px;" align="left">
<br>
<div style="padding:4px;width:175px;height:75px;border:thin solid black;overflow:hidden;">
I am the better select list:<br><br>
<select name="someDropDown" id="someDropDown" class="ctrDropDown" onBlur="this.className='ctrDropDown';" onMouseDown="this.className='ctrDropDownClick';" onChange="this.className='ctrDropDown';">
    <option value="">This is option 1.</option>
    <option value="">This is the second option, a bit longer.</option>
    <option value="">Drink to me only with thine eyes, and I will pledge with mine...</option>
</select>
</div>


One More Note:
This solution is a little bit less than perfect in that if you click the select list and don't choose a NEW value, the class doesn't switch back until you actually blur, or click off of, the select list. I have been unable to find a solution to that one anomaly, so if anybody out there has an idea, please do share!

Hope this helps somebody.

Doug out.
Posted by dougboude at 11:31 AM | PRINT THIS POST! | Link | 40 comments
09 March 2008
Client-Side Drilldowns Made Easy
Last September I shared a post on an alternative to Ajax for client-side interactivity leveraging Coldfusion's WDDX. I'd like to take it a step further now and share an approach (and corresponding code) I often use in my Model-Glue apps when needing to create tiered or drilldown-type select lists withOUT having to make numerous calls to the server. The gist of this methodology is the same as in my previous post:
  1. Retrieve all needed data sets for populating the drilldowns;
  2. Convert those CF queries to a form that Javascript can manipulate;
  3. Write the necessary functions to populate the dropdowns based on previous options selected.

Here is the working sample. Go ahead, play with it!





The main difference in this example is the fact that I'm not utilizing WDDX, but rather JSON to make the data Javascript friendly.

Let me show you what the queries look like for each tier of this particular drilldown:
Tier 1Tier 2Tier 3

An important item of note here is that the data set for a given tier must include the parent ID of the previous tier in order to perform the filtering operations you'll see soon.

Okay, so how to get the queries into a format Javascript can manipulate. The route I chose to go was to convert the queries to JSON via one of two methods, depending on what version of CF you're using. Since not everyone is using version 8 yet, I made this example compatible with anything 6 or higher (versions previous to 8 are dependent on outside conversion; I chose to use JSON.CFC. Version 8 can utilize the built in function "serializeJSON"). Here's the statement where the queries are transformed (using the commonly known custom tag "QuerySim" to create the data for this example):

<cf_querysim>
    level_1
    level_1_id,name
    1|Colors
    2|Shapes
    3|Foods
</cf_querysim>
<cf_querysim>
    level_2
    level_2_id,level_1_id,name
    1|1|Red
    2|1|Blue
    3|1|Yellow
    4|2|Triangle
    5|2|Square
    6|2|Circle
    7|3|Bread
    8|3|Meat
    9|3|Fruit
</cf_querysim>
<cf_querysim>
    level_3
    level_3_id,level_2_id,name
    1|1|Fuschia
    2|1|Brick Red
    3|1|InfraRed
    4|2|Teal
    5|2|Cyan
    6|2|Navy Blue
    7|3|Light Yellow
    8|3|Dark Yellow
    9|4|Isosceles
    10|4|Equilateral
    11|4|Right Triangle
    12|5|Rectangle
    13|5|Parallelogram
    14|6|Ellipse
    15|6|Oval
    16|7|Matzah
    17|7|Hot Cross Buns
    18|7|Brioche
    19|8|Steak
    20|8|Fajitas
    21|8|Hamburger
    22|9|Kiwi
    23|9|Grapes
    24|9|Oranges
</cf_querysim>

<!--- convert our query data to JSON strings...mind the second parameter to the serializeJSON function... --->

<cfif listfirst(SERVER.ColdFusion.ProductVersion) gt 7>
    <cfset level_2_json = serializeJSON(level_2, true) />
    <cfset level_3_json = serializeJSON(level_3, true) />
<cfelse><!--- we're on less than version 8. use json.cfc --->
    <cfset objJSON = createobject("component","json") />
    <cfset level_2_json = replace(objJSON.encode(level_2),"data","DATA","all") />
    <cfset level_3_json = replace(objJSON.encode(level_3),"data","DATA","all") />
</cfif>

<script>
    //set our json data into Javascript objects
    var lev2data = <cfoutput>#level_2_json#</cfoutput>;
    var lev3data = <cfoutput>#level_3_json#</cfoutput>;
</script>

You'll notice in the section where I rely on JSON.CFC, I am doing a replace on the lower case word "data" to make it upper case. This is to make the JSON string produced consistent with the one produced utilizing serializeJSON. Since Javascript is case sensitive, case consistency in the JSON is required if you wish to utilize only one javascript function to perform the select list population. You'll also notice the use of the optional secondary parameter in the serializeJSON function call. This is needed in order to produce a JSON string that can be accessed by Javascript exactly the same as the JSON.CFC string.

MG NOTE: Regarding the fact that I utilize this approach in Model-Glue apps...I have my controller return an already formatted JSON string to my view rather than return a query and then perform the transformation there. Many of my controller methods have an optional "returnJSON" argument that I use when I need a JSON string back rather than a query.

Okay, data sets are available to Javascript. Now to write a few Javascript functions that can spin through that data and populate the appropriate select list. Here are the functions needed to perform the necessary tasks:

<script>
    //function to repopulate targeted select list
    function repopulate(targetObjID,targetDataSet,selectedIDVal, idColName, valColName, optionValColName){
        /*
         parameters:
         targetObjID - the ID of the select list we want to populate;
         targetDataSet - the actual Javascript data object we created previously;
         selectedIDVal - the name of the column in this data set that contains the parent record ID
         idColName - the name of the column that contains THIS tier's own record ID
         valColName - the name of the column that contains the data we want to display as the option text in the dropdown;
         optionValColName - the name of the column that contains the value we want to use as the new options's VALUE value;
        */
       
        //loop over the data object. for every object with a keyname of idval, add it to the dropdown
        var objTarget = document.getElementById(targetObjID);
        ResetObject(objTarget);
        for(i=0;i<targetDataSet.DATA[idColName].length;i++){
            if(targetDataSet.DATA[idColName][i] == selectedIDVal){
                objTarget[objTarget.options.length] = new Option(targetDataSet.DATA[valColName][i].substring(0,45),targetDataSet.DATA[optionValColName][i],false,false);
            }
        }
    }
    //function to clear a dropdown
    function ResetObject(objTarget){
            objTarget.options.length=0;
            objTarget.options[0] = new Option("---------------","",false,false);
            return;
    }

function resetAll(objIDList){//empty out all of the dropdowns specified in objIDList
        var idlist = objIDList.split(",");
        for (i=0;i<idlist.length;i++){
                ResetObject(document.getElementById(idlist[i]));
        }
        return;
}
    
</script>


Of note is the fact that we are working with three select lists. The first select list is always populated at load time and needs no Javascript intervention at all. Select lists "level_2" and "level_3", however, are being completely manipulated by the JS calls by one generically written function.

Last but not least, the HTML with the Javascript calls embedded in the select lists' onChange event:

<body>
    <h2>Client-Side Drilldown Example</h2>
    Level 1: <select name="level_1" id="level_1" onChange="resetAll('level_2,level_3');repopulate('level_2',lev2data,this.options[this.selectedIndex].value,'level_1_id','name','level_2_id');">
        <option value=""><cfoutput>#repeatstring("-",15)#</cfoutput></option>
        <cfoutput query="level_1">
            <option value="#level_1_id#">#name#</option>
        </cfoutput>
    </select>
    <hr width="25%" align="left">
    Level 2:
    <select name="level_2" id="level_2" onChange="resetAll('level_3');repopulate('level_3',lev3data,this.options[this.selectedIndex].value,'level_2_id','name','level_3_id');">
        <option value=""><cfoutput>#repeatstring("-",15)#</cfoutput></option>
    </select>
    <input type="button" value="show ID" onClick="alert(document.getElementById('level_2').options[document.getElementById('level_2').selectedIndex].value);">
    <hr width="25%" align="left">
    Level 3:
    <select name="level_3" id="level_3" >
        <option value=""><cfoutput>#repeatstring("-",15)#</cfoutput></option>
    </select>
    <input type="button" value="show ID" onClick="alert(document.getElementById('level_3').options[document.getElementById('level_3').selectedIndex].value);">
</body>

Voila! That's it! instant, client-side drilldown with only a single call to the server!

Hope it saves someone a little time. ;)

Doug out.
Posted by dougboude at 10:46 PM | PRINT THIS POST! | Link | 0 comments
05 July 2007
DEMYSTIFYING JSON (for myself)
I'm doing this post because the term 'JSON' has continued to appear here and there within blog posts, conference sessions, articles, and emails that I consume as part of my professional growth regimen. Despite the fact that the term is so very often mentioned casually as if everybody has known about it since Kindergarten, the greater part of my understanding of JSON is barren except for the few clues I have managed to glean through context. So, I decided to take the time to get to know JSON a little more intimately, and learned some interesting things.

WHAT I THOUGHT IT WAS
Based solely on the info I managed to gather from "between the lines", I knew that JSON stood for JavaScript Object Notation, and that it was an alternative to XML when dealing with the results of Ajax calls. Knowing that much, I could deduce that it was basically "data in a string". But what it looked like, how to handle it, and why I would want to do it were still questions in my mind.

WHAT I FOUND IT TO BE
JSON is indeed a string representation of simple or complex data, just as is XML, only without the tags AND without the need to treat that string as a document type in order to transverse it elegantly. JSON contains two indicators: curly braces to indicate that a structure follows, and square brackets to indicate an array. Here's a structure written in JSON: {key1:"val1",key2:"val2",key3:"val3"} and a one dimensional array: ["val1","val2","val3"]. You can nest these types within one another, too; for example, the value of a structure key could be an array, and would be written as this: {key1:["val1","val2","val3"],key2:"val2"}. Pretty cool, eh?

Now, how to get data back and forth between JSON and raw CF types. It's a no-brainer using a tag made available as an open source project on RIA Forge by Andrew Powell: CFJSON . I downloaded it and was using it in less than two minutes on a test template. Very simple, very easy.

All of this good information led me to pose more questions, which I went on to get answers to.

  • Were there any performance advantages of using JSON over XML?
  • Was there significant differences in the length of the string produced by a JSON conversion as opposed to an XML conversion (significant in the realm of web services where that string will have to be sent over the wire)?
  • Were there any compelling reasons to adopt JSON in leiu of XML or WDDX?

Following are the results of me finding out the answers to those questions.

THE EXPERIMENT
Working with a 500 row recordset, each row containing 52 fields of mixed data types, including some sql text, convert it to both JSON and XML and record the results of the time it took and the resulting string length. Do this in both a local template/cfc environment AND over the wire via a web service. I used CFJSON as the JSON converter, and Ray Camden's XML.cfc as the XML converter.

THE RESULTS
LOCAL TESTS
1
JSONXML
String Size 539906 1250561
Time (ms) 36374 2046
2
JSONXML
String Size 539906 1250561
Time (ms) 36156 2640
3
JSONXML
String Size 539906 1250561
Time (ms) 37103 2156

WEB SERVICE TESTS
1
JSONXML
String Size 539906 1250561
Time (ms) 36581 2390
2
JSONXML
String Size 539906 1250561
Time (ms) 37832 2496
3
JSONXML
String Size 539906 1250561
Time (ms) 37644 2406

The string size was consistent, as expected. JSON produced a string that was 57% smaller than the same data represented as XML. Even so, the actual difference in the time it takes to send the larger string doesn't even come close to compensating for the additional time it took to produce the shorter JSON string. It took 1,370% LONGER to produce the JSON string than it did the XML string. Wow, at this point it's almost a no-brainer that I would NOT want to use JSON for much if anything. But this huge difference got me to thinking: What was so vastly different about the way the XML was being produced and the way the JSON was being produced? So I dug into the CFCs to find out.

THE DIFFERENCE
Both CFC methods make use of lots and lots of looping and string concatenation. The only glaring difference was in the way this was done. XML.CFC leverages the Java.lang.stringbuffer object and CFJSON uses straight CF string manipulation. So, I altered the portion of the method in CFJSON.CFC that converts queries to utilize the java.lang.stringbuffer object as well. The results were VERY favorable! check out these time tests after I made the switch:

CFJSON.CFC TESTS AFTER STRINGBUFFER CONVERSION
TESTLOCAL TIME(ms)WEB SERVICE TIME(ms)
1 2156 4437
2 2125 3843
3 2031 4022

Wow. The conversion to JSON now runs neck in neck with the XML conversion, simply by leveraging the stringbuffer object. Sweeeeet.

Okay, now that I know that JSON data strings are significantly smaller, just as fast to create, how about manipulation? How painful is that? To find out I decided I was going to use my JSON data in a javascript function. I scaled down the size of the data set to only five records and a handful of fields for this test. Check out how simple it was to 'dump' my data into javascript and to access it afterwards:
<script>
    function showMeSomeJSON(){
        var objQuery = <CFOUTPUT>#jsondata#</CFOUTPUT>;
        var i = 0;
        for(i=0;i<objQuery.recordcount;++i){
            alert(objQuery.data.due_date[i]);
        }
    }
</script>


CONCLUSION
I have come to no solid conclusion as of yet, but I must say that I'm leaning heavily towards JSON in lieu of XML. It's got a smaller footprint all the way around, has the ability to capture complex nesting, and is really straightforward to navigate using Javascript. Additionally, the same way CFJSON can encode complex data, it also DECODES it on the receiving end, turning it back into something that CF recognizes.

Oh, and I think I'm going to email the CFJSON guys and let them know about the significant performance improvement I got when switching to stringbuffer. They may have already experimented in this arena, but just in case, I'll let them know.

That's it for my personal "JSON Demystification". :)

Doug out.

P.S. If you would like to see an actual JSON representation of my five record query, here it is:

{"recordcount":5,"columnlist":"complete,date_desired,dept,details,developer,due_date,est_hours","data":{"complete":["100%","100%","0%","100%","0%"],"date_desired":["Less Than 2 weeks","Less Than 1 week","More Than 2 weeks","Less Than 1 week","Less Than 1 week"],"dept":["Eligibility","Marketing","Benplan_com","Repricing","EDI"],"details":["1. For rehires, if a rehire date is present on the file and the rehire date is greater than the date of hire, then I need to use it.\r\n\r\n2. Cobra reason - make sure the term reason for a benefit is being put on the AE2 file. This is tied in with Scott\'s work.\r\n\r\n3. Some dependents have termed benefits but no effective dates, also no effective date for the employee. I need to find out where these are coming from.\r\n\r\n4. Terms need to be removed from the file 60 days after the employment term date. This will actually be added to the TSS AE2 file as well.","Dave Lawson, SWI, would like for us to send him the annual report we created for him that gives him EE and Dependent data for Group #s 90947 and 90947P.","- We will send?? or receive daily RX claims\/deductible file updates to one or two PBM\'s--Innoviant for sure and maybe Catalyst.\r\n"," 1) Show correct network for each group. 2) Change elapsed days to start counting on the day the claim was sent out. ","Correct the SSN validation on inbound EDI returned repriced claims. "],"developer":["Dan Crouch","Kelly Young","Gemma Anthony","Kelly Billen","Kelly Billen"],"due_date":["09\/09\/2004","8\/19\/04","NA","08\/30\/2004","08\/13\/2004"],"est_hours":["",1,400,1,1 ]}}

Posted by dougboude at 5:50 PM | PRINT THIS POST! | Link | 4 comments
19 March 2007
Using Ajax with Model-Glue - it's really quite simple
At first glance, the topic can seem somewhat overwhelming. The key, however, is to understand each of them separately and then using them together won't seem overwhelming in the least. I'll touch briefly on each of them independently, but will assume for the bulk of this post that you have at least an elementary understanding of what each is and generally how they work.

Ajax refers to the ability we have to make Javascript run back to our web server and retrieve information for us, without reloading the entire page. It is doing nothing more than making an http call to a web page, just like we would do everytime we click a link on a page. The Javascript, however, makes the call in the background, unseen by the user, receives whatever content results from that web page call, and then does something with it inside of our current page.


Model-Glue is a popular Coldfusion application framework that is becoming the "procedural man's tour guide to object oriented programming", breaking "the rest of us" into the world of OO. If you haven't played with it or invested the time to get familiar with it, you have a lot of your peers who HIGHLY recommend it. I recently had the privilege of doing
a presentation for the Kansas City ColdFusion User's Group on Model-Glue that was saved as a Breeze preso if you need or want some kind of intro.

Okay, from this point forward I can safely assume that you have at least a working understanding of Ajax and Model-Glue as separate tools, and will now share with you how the two integrate easily (from the 1,000 foot view). I will NOT be focusing on any one Ajax library, as there are several good ones out there, and as the specific Ajax library being used is (or should be, if the Ajax library is intuitive enough) COMPLETELY IRRELEVANT to understanding how they integrate with one another.

First thing, consider a basic Model-Glue event lifecycle:

1. A Model-Glue event is called via navigation to a URL (index.cfm?event=app.home).
2. The Model-Glue framework looks up the event (in the modelglue.xml file), determines what broadcasts to make, what results to evaluate, and what views to render.
3. Rendered views are returned to the browser.
4. Browser displayes the rendered view.

Hmm, nothing complex here, eh?

Now consider a basic Ajax call to a Model-Glue event:
1. A Model-Glue event is called via Javascript library (var newcontent = ajax.remotecall('index.cfm?event=ajax.getInfo');).
2. The Model-Glue framework looks up the event (in the modelglue.xml file), determines what broadcasts to make, what results to evaluate, and what views to render.
3. Rendered views are returned to the browser.
4. Javascript receives the rendered view and does whatever it was told to do with it (document.getElementById('ajaxDiv').innerHTML = newcontent;)!!

So what is different when incorporating Ajax into the mix? Very, very little. The only REAL difference that I've found I need to keep in mind is in regard to what my view contains when the event I'm calling was specifically created for an Ajax call. For instance, in a "normal" event call, i'm typically going to be returning an entire html page, complete with body tags and the works. In an Ajax call, it will be either a chunk of html or some kind of Javascript-ready data (XML, JSON data, etc.) I have found that it's simpler, whenever possible, to return a chunk of pre-rendered HTML and have my client-side javascript simply place the returned html into the target div. Working with returned XML is where Ajax will become more complex, but even then, the complexity is STILL on the client side JS coding and not anything to do with Model-Glue at all.

I don't believe that there's anything else a person needs to know in order to be able to "think about" and visualize the interaction between Model-Glue and Ajax...it really is straightforward stuff! Not sure why just a few months ago it sounded like such a difficult concept to me....

Here's an illustration showing how Ajax would work with a Model-Glue page:



The actual Javascript that would handle the call could be as simple as the following example:
<!--- First, we load up the ajax library... --->
<script src="/js/AjaxLibrary.js" type="text/javascript"></script>

<!--- Now define the function we'll be using when the user clicks our "Ajaxified" link... --->
<script>         
function gotClicked(){
    /*first, retrieve the new content ... */
    var newcontent = ajax.remotecall('index.cfm?event=ajax.getInfo');
    /*now stuff the newly retrieved content into our ajaxDiv... */
    document.getElementById('ajaxDiv').innerHTML = newcontent;
}
</script>

That's it, really! Nothing more to it. After grasping this very basic understanding, the only thing left to do now is to select an Ajax library (personally, I take a shine to Scriptaculous...) and start learning how to use it!

Doug out.

Posted by dougboude at 11:34 PM | PRINT THIS POST! | Link | 6 comments
18 September 2006
Client-Side Interactivity without Ajax
Keeping response times down and interactivity high has and always will be two important priorities with web interfaces of any kind. For standard html interfaces, Ajax is all the buzz and is great when it’s necessary to maintain interaction with live data. But when a static version of the data will do just fine, there’s at least one other alternative that you may want to consider….

The <CFWDDX> tag provides a way for us to bridge the gap between dynamic data processed on the server side (specifically, queries returned by CFQUERY), and Javascript on the client side. What this does is give us the ability to transform query results into a form that we can manipulate via javascript, opening up some very useful potential for interactive Dynamic HTML.

In a nutshell, the process goes like this:
  • A CFQUERY tag is run;
  • The result is fed to CFWDDX with the ACTION attribute set to CFML2JS, which produces a Javascript-ready recordset;
  • The JS function that works with the recordset is created
That’s it. Now, for some of the nitty gritty details

In my example, I’m displaying a list of employees for the user to select from. What I want though is that whenever an employee is selected from the list, their detailed information is displayed to the side so that the user can determine if this is the right person or not. Like so:

(go 'head! click it!)

 

Ajax would work for this! Everytime an employee’s name is clicked on, we could make a call back to the server and grab that employee’s detailed information, then, using Javascript, output it to the designated portions of the template without ever reloading the page (yet still executing an HTTP call over the web). Or, we could just go ahead and retrieve the employees’ details during our initial query, since we had to retrieve their names and IDs anyway in order to produce the select list.
<!--- Using QuerySim to create the cfquery... --->
<cf_querysim>
    UserInfo
    userid,fname,lname,ext,title,dept,email
    200|Joe|Blow|2235|Supervisor|Reporting|jblow@nowhere.com
    300|Doug|Boude|2112|WaterBoy|Recreation|dboude@nowhere.com
    100|Stan|Cox|1225|Developer|IT|scox@nowhere.com
    400|Jim|Pickering|1513|Designer|IT|jpickering@nowhere.com
    500|John|Smith|1112|CEO|Management|JASmith@nowhere.com
    600|John|Smith|1123|Janitor|Building Maintenance|JLSmith@nowhere.com
</cf_querysim>

We could then take that query and create a Javascript-friendly version of it like so:
<script>
    <cfwddx action="cfml2js" input="#UserInfo#" toplevelvariable="users">
....
</script>
(Note that the attribute “TopLevelVariable” contains the value that will be used to name our recordset; that is the name it will be referenced as within our JS function.)

Within the body of our page, we’ll go ahead and create the select list just like we always do, only let’s add a function call to the onClick event that we can use to dynamically populate the targeted portions of our template:
<select onClick="getUserInfo(this.value);" size="8">
    <option value="" selected></option>
    <cfoutput query="userInfo">
        <option value="#userid#">#lname#, #fname#</option>
    </cfoutput>
</select> 

Additionally, let’s create the placeholders for our employee details:
<table>
    <tr>
        <td><STRONG>First Name:</STRONG></td><td><span id="fname"></span></td>
    </tr>
    <tr>
        <td><STRONG>Last Name:</STRONG></td><td><span id="lname"></span></td>
    </tr>
    <tr>
        <td><STRONG>Title:</STRONG></td><td><span id="title"></span></td>
    </tr>
    <tr>
        <td><STRONG>Department:</STRONG></td><td><span id="dept"></span></td>
    </tr>
    <tr>
        <td><STRONG>Extension:</STRONG></td><td><span id="ext"></span></td>
    </tr>
    <tr>
        <td><STRONG>Email:</STRONG></td><td><a id="email" href=""></a></td>
    </tr>
</table>
(Note: In my example, I use SPAN tags as placeholders as well as an Anchor tag, but any valid document object (form fields, div tags, etc.) can be targeted as placeholders, as long as they have a unique ID value)

At this point, our query has been converted into a ‘WDDXRecordset’ object. Behind the scenes, it’s a complex Javascript array, but the nice folks at Adobe wrapped it up for us with a set of functions that make it easy to get at the data using JS.  For example, to retrieve the first name from the first row, the call would look something like
Var thisName = getField(0,’fname’);
....
(Note that our first row is referenced with zero instead of one.)

Now all we need is a function to retrieve the employee details. It’s as simple as a Javascript FOR loop, leveraging the functions provided by the WDDXRecordset object:
function getUserInfo(userid){
    var i = 0;
    for (i=0; i < users.getRowCount(); i++){// loop through our wddx recordset...
        if(users.getField(i,'userid') == userid){//if the current record matches the ID passed in...
            document.getElementById('fname').innerHTML = users.getField(i,'fname');
            document.getElementById('lname').innerHTML = users.getField(i,'lname');
            document.getElementById('title').innerHTML = users.getField(i,'TITLE');
            document.getElementById('dept').innerHTML = users.getField(i,'dept');
            document.getElementById('ext').innerHTML = users.getField(i,'ext');
            document.getElementById('email').href = 'mailto:' + users.getField(i,'email');
            document.getElementById('email').innerHTML = users.getField(i,'email');
            return false; //ends this function call
        }
    }
    //if we made it here, then we found no match in our loop above; just blank everything out.
    document.getElementById('fname').innerHTML = "";
    document.getElementById('lname').innerHTML = "";
    document.getElementById('title').innerHTML = "";
    document.getElementById('dept').innerHTML = "";
    document.getElementById('ext').innerHTML = "";
    document.getElementById('email').href = "";
    document.getElementById('email').innerHTML = "";
}  
(Note: Although our function only utilized the getField() and getRecordCount() methods,  several more are available to us. You can take a gander at what else the WDDXRecordset provides at this LiveDocs link)

You can grab the entire template here if you'd like to see it all together.

Gotchas/things to remember:
  • some web hosts don't provide access to the CFIDE/Scripts directory. If this is the case, then you'll need to grab a copy of wddx.js and put it in your webroot, then reference it within a script tag prior to creating any wddx-powered functions, like so:
<script src="wddx.js"></script>//< - - - making sure wddx.js is loaded...
<script>
    <cfwddx action="cfml2js" input="#UserInfo#" toplevelvariable="users">
    function getUserInfo(userid){
        ...(remainder of js)
   
  • When referencing query fields, the js is not case sensitive (a getField(1,'title') will work as well as getField(1,'TITLE') ); However, javascript itself IS case sensitive, so referencing the target span tag you must use the exact case of the ID (getElementById('title') is NOT the same as getElementById('TITLE') )
  • The WDDXRecordset object contains a zero-based array, meaning that the index number of the first row will actually be referenced as zero rather than one (getField(0,’fname’) retrieves the value for first name from the first record)
That's it!

Doug out 
Posted by dougboude at 7:49 PM | PRINT THIS POST! | Link | 3 comments
24 August 2006
Cool Eclipse Plugin for CSS and JS

Aptana is an Eclipse plugin that came to me highly recommended by a peer (Doug Sims) and has proven itself to be a real assett to me as well. What does it do? Only provide code assist with both Javascript and CSS files! Another VERY COOL thing it does within the code assist is provide visual indicators of which CSS and Javascript attributes are IE and/or Firefox compatible, something that is a constant plague to those trying to maintain cross-browser compatibility.

and now for something completely different...visit
its all MY effing fault!

Visit Aptana.com and click the "Download Aptana Plugin" link on the right hand side of the page for instructions on how to install.

One caveat of this plugin is that it's code assist feature is only available when working with .JS or .CSS files...inline css or javascript, well, you're on your own.

Posted by dougboude at 12:28 PM | PRINT THIS POST! | Link | 2 comments
11 May 2006
Don't you hate it when...
Kill Bill's Browser
Okay, here's the scene:

You're standing in a crowded 4 star hotel lobby, leaning against the 10 ft fountain in the center. In your hand you are holding a javascript array with 5 items in it, the last of which has a null value. So you ask the little Firefox guy on your left shoulder to tell you how many items are in your array. He looks it over, sees that the last item has a null value, and so tells you that your array has 4 items in it. Then you turn to the little IE guy on your right shoulder and ask him the same question. He looks it over, sees that there are 5 items in the array, and reports back that indeed your array contains 5 items. So, you ask the little IE guy on your right shoulder to please fetch you the 5th item in your array, and he immediately vomits all over your good coat and tie, because for the LIFE of him, he can't find a fifth item in your array! Despite the fact that he emphatically stated there were definately 5 items in your array, he lacks the testicular fortitude to be able to fetch the sucker. Now, were you to ask the Firefox guy on your left shoulder to fetch you the fifth item, he would politely remind you that there is no fifth item. But does Bill's browser do that? NOOOOOoooooo, it just makes all the time you spent writing your javascript function a total waste, because now you have to go BACK into it and re-code the logic to account for the fact that Bill's browser is going to behave like a drunken, double-visioned sailor on his last night of shore leave. Don't you hate that? Why can't Bill's browser just conform? The alternative is to do your part to help kill BIll's browser; download Firefox today and set yourself free. I highly recommend it.
That's my rant for this evening.
Posted by dougboude at 1:04 AM | PRINT THIS POST! | Link | 4 comments