ColdFusion & AJAX: Converting ColdFusion Objects to Javascript Objects

Posted June 23, 2006 at 8:06 AM

Tags: AJAX, ColdFusion, Javascript / DHTML

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

Comments (0)  |  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


Home   |   Web Log   |   ColdFusion   |   Projects   |   Resume   |   Job Form   |   Search   |   Contact
Epicenter Consulting - Custom Software Solutions for Business Evolution HostMySite.com - The Leader In ColdFusion Hosting