ColdFusion & AJAX: Converting ColdFusion Objects to Javascript Objects

Posted June 23, 2006 at 8:06 AM

Tags: ColdFusion, Javascript / DHTML, AJAX

As I talked about earlier, I am starting to get into AJAX and, as a hands-on kind of guy, I do my best learning from building. So, now that I have my XmlHttpRequest wrapper, I have created some functions for the server side of things that convert ColdFusion objects into Javascript objects that are going to be passed back to the browser.

If you remember from before, my AJAX wrapper is designed to evaluate the data returned from the server via the XmlHttpRequest (ex. eval( objConnection.responseText )). Javascript is a wicked-sweet interpreted language and I have found it very convenient to return Javascript code that will be evaluated into objects that most closely resemble their ColdFusion counterparts. The following methods are used to convert ColdFusion queries and structs to Javascript objects.

This method takes a ColdFusion query and return the Javascript code that would be used to model it via an array of objects. It returns a string in the form of: new Array(new Object(), new Object(), new Object()):

 Launch code in new window » Download code as text file »

  • <cffunction name="QueryToArray" access="public" returntype="string" output="false"
  • hint="Converts a query into Javascript code for an array of structures. Will return strings in the form of 'new Array(new Object(), new Object()....);'">

  • <!--- Define arguments. --->
  • <cfargument name="Data" type="query" required="true" />

  • <cfscript>

  • // Define the local scope.
  • var LOCAL = StructNew();

  • // Convert the column list to an array for faster parsing.
  • LOCAL.ColumnList = ListToArray( ARGUMENTS.Data.ColumnList );

  • // Create the string buffer for creating the response string.
  • LOCAL.ResponseBuffer = CreateObject( "java", "java.lang.StringBuffer" );

  • // Start the array. This array will be an array of objects.
  • LOCAL.ResponseBuffer.Append( "new Array(" );

  • // Loop over the query and create an object for each for.
  • for (LOCAL.Row = 1 ; LOCAL.Row LTE ARGUMENTS.Data.RecordCount ; LOCAL.Row = (LOCAL.Row + 1)){

  • // Create the row object.
  • LOCAL.ResponseBuffer.Append( "{" );

  • // Loop over the columns to create the object values.
  • for (LOCAL.Column = 1 ; LOCAL.Column LTE ArrayLen( LOCAL.ColumnList ) ; LOCAL.Column = (LOCAL.Column + 1)){

  • // Get the key.
  • LOCAL.Key = LOCAL.ColumnList[ LOCAL.Column ];

  • // Get the value.
  • LOCAL.Value = ARGUMENTS.Data[ LOCAL.Key ][ LOCAL.Row ];

  • // Add the pair. Escape the value so that is doesn't break the string. This requires the
  • // use of the "\" before single quotes, double quotes, and backward slashes (which
  • // ordinarily would be special characters in Javascript).
  • LOCAL.ResponseBuffer.Append( LCase( LOCAL.Key ) & ":""" & REReplace( LOCAL.Value, "(""|\\)", "\\\1", "ALL" ) & """" );

  • // Check to see if we need to add the comma.
  • if (LOCAL.Column LT ListLen( ARGUMENTS.Data.ColumnList )){
  • LOCAL.ResponseBuffer.Append( "," );
  • }

  • }

  • // Close the row object.
  • LOCAL.ResponseBuffer.Append( "}" );

  • // Check to see if we will have more objects to add.
  • if (LOCAL.Row LT ARGUMENTS.Data.RecordCount){
  • LOCAL.ResponseBuffer.Append( "," );
  • }

  • }

  • // End the array.
  • LOCAL.ResponseBuffer.Append( ")" );

  • // Return the string.
  • return( LOCAL.ResponseBuffer.ToString() );

  • </cfscript>
  • </cffunction>

On the Javascript side, you could use this evaluated object in the same manner you would a query:

 Launch code in new window » Download code as text file »

  • // Loop over the rows in the "query" which is now an array.
  • for (var intRow = 0 ; intRow < arrResults.length ; intRow++){
  •  
  • // Loop over each column and alert.
  • for (var strColumn in arrResults[ intRow ]){
  • alert( strColumn + " : " + arrResults[ intRow ][ strColumn ] );
  • }
  •  
  • // Or, you can call columns directly.
  • alert( arrResults[ intRow ].id );
  • alert( arrResults[ intRow ].name );
  •  
  • }

The following method takes ColdFusion structures and creates the Javascript code for creating an object in the form of: new Object({ a:3, b:7, ....}). As I stated before, I am not thinking about nested objects just yet. I will cross that bridge shortly. For the moment, while I am learning about AJAX, I am just going to work with structures containing simple values:

 Launch code in new window » Download code as text file »

  • <cffunction name="StructToObject" access="public" returntype="string" output="false"
  • hint="Converts a ColdFusion struct of SIMPLE values into a Javascript object. Will return strings in the form of: new Object({})">

  • <!--- Define arguments. --->
  • <cfargument name="Data" type="struct" required="true" />

  • <cfscript>

  • // Define the local scope.
  • var LOCAL = StructNew();

  • // Create the string buffer for creating the response string.
  • LOCAL.ResponseBuffer = CreateObject( "java", "java.lang.StringBuffer" );

  • // Start the object.
  • LOCAL.ResponseBuffer.Append( "{" );

  • // Get the key count.
  • LOCAL.KeyCount = StructCount( ARGUMENTS.Data );

  • // Get an index for keeping track of the key usage.
  • LOCAL.KeyIndex = 1;

  • // Loop over the keys to create the object values.
  • for (LOCAL.Key in ARGUMENTS.Data){

  • // Get the value.
  • LOCAL.Value = ARGUMENTS.Data[ LOCAL.Key ];

  • // Add the pair. Escape the value so that is doesn't break the string. This requires the
  • // use of the "\" before single quotes, double quotes, and backward slashes (which
  • // ordinarily would be special characters in Javascript).
  • LOCAL.ResponseBuffer.Append( LCase( LOCAL.Key ) & ":""" & REReplace( LOCAL.Value, "(""|\\)", "\\\1", "ALL" ) & """" );

  • // Check to see if we need to add the comma.
  • if (LOCAL.KeyIndex LT LOCAL.KeyCount){
  • LOCAL.ResponseBuffer.Append( "," );
  • }

  • // Add one to the key index.
  • LOCAL.KeyIndex = (LOCAL.KeyIndex + 1);

  • }

  • // End the object.
  • LOCAL.ResponseBuffer.Append( "}" );

  • // Return the string.
  • return( LOCAL.ResponseBuffer.ToString() );

  • </cfscript>
  • </cffunction>

On the Javascript side of things, you can reference the structure keys just as you would in ColdFusion:

 Launch code in new window » Download code as text file »

  • alert( objData.id );
  • alert( objData.name );

Next step is to create a recursive function that can handle nested data types.

Now, you might be thinking, why not just use CFWDDX to convert the ColdFusion objects to Javascript objects? One, cause I am learning more by making my own conversion. Two, CFWDDX has to create a top-level Javascript variable to hold the data, which is completely unnecessary in AJAX. And Three, CFWDDX is just not that efficient (in terms of code size). Take for instance trying to convert the following ColdFusion struct to a Javascript object:

 Launch code in new window » Download code as text file »

  • <cfset objTest = StructNew() />
  • <cfset objTest.id = 1 />
  • <cfset objTest.name = "ben nadel" />
  • <cfset objTest.rich = false />
  • <cfset objTest.smart = true />
  • <cfset objTest.girlfriend = "molly" />
  • <cfset objTest.mood = "freakin' sweet" />

Using the CFWDDX - CFML2JS, we would get Javascript code like:

 Launch code in new window » Download code as text file »

  • objData = new Object();
  • objData["mood"] = "freakin\' sweet";
  • objData["girlfriend"] = "molly";
  • objData["name"] = "ben nadel";
  • objData["id"] = "1";
  • objData["rich"] = "false";
  • objData["smart"] = "true";

That's 193 characters to return. Using my method, we would get Javascript code like:

 Launch code in new window » Download code as text file »

  • new Object({
  • mood:"freakin\' sweet",
  • girlfriend:"molly",
  • name:"ben nadel",
  • id:"1",
  • rich:"false",
  • smart:"true"
  • })

That's 106 characters to return. That's a difference of about a half the characters that the server will have to return to the browser. Currently, my methods are not the best at projecting crazy keys like the CFWDDX is, but that will change shortly.

What's frustrating is that in the QueryToArray() method, when I am creating an object, I can use just { } notation since it is being evaluated as part of a parent array; however, I can't just return "{}" in the StructToObject() method as it doesn't know to evaluate directly to an Object. Hence, the use of "new Object()" in the latter method.

Download Code Snippet ZIP File

Post Comment  |  Ask Ben  |  Permalink  |  Other Searches  |  Print Page




Reader Comments

There are no comments posted for this web log entry.


Post Comment  |  Ask Ben

Recent Blog Comments
Nov 7, 2009 at 5:53 PM
Ask Ben: Javascript String Replace Method
You can find here an advanced function that prepared with javascript replace function. This can make the first letters of words, sentences, lines and whatever you define automatically: http://www.m ... read »
Andrew Neely
Nov 7, 2009 at 4:56 PM
A Moment That Touched Me - The Fountainhead
Ben, Glad you enjoyed the podcast. Yeah, the Tank Riot guys can get really chatty during the episodes, but that's part of the charm of it for me. They've covered everything from Nichola Tesla to Cha ... read »
Nov 7, 2009 at 4:43 PM
Building A Fixed-Position Bottom Menu Bar (ala FaceBook)
Is it possible to make some more MenĂ¼`s ? ... read »
Jill
Nov 7, 2009 at 11:40 AM
How To Unformat Your Code (Like A Pro)
Derek, I think you might be right - sweet! Thanks for the link :) ... read »
Nov 7, 2009 at 11:25 AM
How To Unformat Your Code (Like A Pro)
I think it would be way easier to just use this http://www.logichammer.com/html-formatter/ He just released v3 and it rocks. ... read »
Jill
Nov 7, 2009 at 7:58 AM
How To Unformat Your Code (Like A Pro)
LMAO - this was pretty funny! I have to admit - I also love to reformat code so I can read it. My boss used to tell me to leave my OCD at home. Now I don't feel so bad after reading everyone else' ... read »
Nov 6, 2009 at 10:10 PM
How To Unformat Your Code (Like A Pro)
The timing of this post is just uncanny. I spent the last 15-20 minutes manually un-formatting my "Ben Nadel" style code within a CFC of mine. I was really digging the readability a few weeks ago, bu ... read »
Roe
Nov 6, 2009 at 5:11 PM
Passing Arrays By Reference In ColdFusion - SWEEET!
ArraySort also reorders the results of these java obj's ... read »