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
};
}
});


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;)
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.