ColdFusion 8 Json Return Format and ExtJS

Yes, I am really enjoying getting back into JavaScript. The language really has changed since the days I was heavier into it (1998 thru 2001, maybe), so it's great to see how much it has grown. I feel like I've been ignoring an old friend.

One of the great things, now, are these incredible libraries for crossbrowser DOM manipulation and extended display components, like JQuery and ExtJS. I'm using both quite a bit, and really like not having to fight with browser incompatibility (most of the time). I use JQuery for most DOM manipulation tasks, while using Ext for it's extensive, and consistent, component set.

Ext components use data 'Store's to populate many of their objects, such as combo boxes, data grids, and the like. Ext provides a number of 'Store' types, and 'reader's for parsing through them. Basically, you can provide simple arrays, XML, or Json to create a 'Store', which is a collection of individual records. When dealing with Ajax calls, XML can get heavy, but Json can be pulled natively from ColdFusion functions, just by including "returnformat=json" to your requests. The problem, though, is that the existing JsonStore object, within Ext, can't read the return in the format that ColdFusion provides.

Now, you know this has to work somehow. After all, ColdFusion 8's built in components are all Ext 1.1, so there must be some way. Well, I don't know what CF is doin' under the hood, but a quick trip to Google showed that someone else had already dealt with this issue, and parked a solution right on the Ext Forums. John (Daemach) Wilson wrote a custom extension to the base Store 'reader' object.

John's implementation required the developer to use the QueryConvertForGrid() method on your function returns, but I like to repurpose code as much as I can. I modified John's code, just slightly, to allow for a basic "returnformat=json" type of call (or the QueryConvertForGrid() method, if you want it). You can find basic usage information on the original forum post, plus my modified extension below:

/*
* Ext JS Library 2.0
* Copyright(c) 2006-2007, Ext JS, LLC.
* licensing@extjs.com
*
* http://extjs.com/license
*/
/*
* John Wilson (Daemach) daemach@gmail.com
* http://ideamill.synaptrixgroup.com
*
*/
Ext.data.CFJsonReader = function(recordType, meta){
this.meta = meta || {};
this.recordType = Ext.data.Record.create(recordType);
};

Ext.extend(Ext.data.CFJsonReader, Ext.data.DataReader, {
read: function(response){
var json = response.responseText;
      if(json.indexOf('"QUERY":') < 0){
         json = '{"QUERY":' + json + '}';
      }
var o = eval("(" + json + ")");
if (!o) {
throw {
message: "CFJsonReader.read: Json object not found"
};
}
      if (o.metaData) {
delete this.ef;
this.meta = o.metaData;
this.recordType = Ext.data.Record.create(o.metaData.fields);
this.onMetaChange(this.meta, this.recordType, o);
}
return this.readRecords(o);
},

// private function a store will implement onMetaChange: function(meta, recordType, o){

},
readRecords: function(json){
var aRecords = [];
var cList = json.QUERY.COLUMNS;
var cData = json.QUERY.DATA;
var idField = this.meta.id;

for (var i = 0; i < cData.length; i++) {
var oRecord = {};

for (var j = 0; j < cList.length; j++) {
oRecord[cList[j]] = cData[i][j];
}
if (idField) {
var id = cData[i][cList.indexOf(idField)];
aRecords.push(new Ext.data.Record(oRecord, id));
} else {
aRecords.push(new Ext.data.Record(oRecord));
}
}
      if(!json.TOTALROWCOUNT){
         json.TOTALROWCOUNT = aRecords.length;
      }
return {
success: true,
records: aRecords,
totalRecords: json.TOTALROWCOUNT
};
}
});

Comments
Raymond Camden's Gravatar Odd - why not just also use the queryFormat attribute? I use Spry w/ CF's returnType=JSON all the time, w/o needing to do something special in the CF code. I just use the queryFormat attribute as well. I think you should look into that.
# Posted By Raymond Camden | 5/6/08 7:01 AM
Steve 'Cutter' Blades's Gravatar @Ray,

Yeah, I had never heard of the 'queryFormat' attribute until you brought it up. After some digging I'm not surprised, it's buried fairly well in the Developer's Documentation. I did some experimenting and found that I would still need to define a custom reader to properly parse the CF8 return format, regardless of setting queryFormat to 'row' (current default) or 'column'.

It's in the way that Ext's JsonReader expects the data to be formatted. Basically, it's expecting (by default) an array (the query) of stuctures (each record), in which each member of the structure is a key/value pair, with the key being the column name (go figure). The old CFJSON object, from epiphantastic.com, returned CF objects in this type of layout by default. But, that format is a bit heavy, as you repeat your column names over and over again for each record. Luckily, Ext provides a way to extend it's base Reader class, defining custom readers to go outside their normal default data layout.

I'm using this directly with Ext 2.1. All of the built-in CF8 Ajax components have their stuff under the hood, so luckily they don't require any of this;)
# Posted By Steve 'Cutter' Blades | 5/6/08 10:28 AM
Cat's Gravatar Hello! You answered a question of mine on the extJS forums.

As you seem like a rather knowledgeable fella, I've got a semi-related question for you:
How are you dealing with security on access="remote" cfc functions? I've been poking around at answers I've found on google, but I'd like to know what you've come up with.
# Posted By Cat | 5/8/08 4:39 PM
BlogCFC v. 5.8.001 was created by Raymond Camden. Layout inspired by bluerobot.com., with some JQuery thrown in for fun.