I'm trying to pick up AngularJS with a ColdFusion back end and am running into a few roadblocks. I am modifying their "To Do" app http://angularjs.org/ with the CF Art Gallery database. I'm trying to link a ColdFusion CFC to an Angular app using AJAX.
Below is my artists.cfc:
<cfcomponent>
<cffunction name="getArtists" access="remote" >
<cfargument name="firstName" default="">
<cfargument name="lastName" default="">
<cfquery name="getArtists_sql" datasource="cfartgallery">
SELECT
firstname as text,
lastname as done
FROM artists
WHERE 0=0
<cfif firstName neq "">
AND ucase(firstname) like ucase('%#FIRSTNAME#%')
</cfif>
<cfif lastName neq "">
OR ucase(lastname) like ucase('%#LASTNAME#%')
</cfif>
</cfquery>
<cfreturn getArtists_sql>
</cffunction>
</cfcomponent>
I call the CFC using AngularJS with the following code:
function TodoCtrl($scope, $http) {
$http.get('cfc/artists.cfc?method=getArtists&returnformat=json').
success(function (response) {
$scope.todos = data.DATA;
}).
error(function (data) {
$scope.todos = data;
});
}
I know that I get a response back. Below is the JSON string Chrome's developer tools returns for me:
{
"COLUMNS":
["TEXT","DONE"],
"DATA":[
["Aiden","Donolan"],
["Austin","Weber"],
["Elicia","Kim"],
["Jeff","Baclawski"],
["Lori","Johnson"],
["Maxwell","Wilson"],
["Paul","Trani"],
["Raquel","Young"],
["Viata","Trenton"],
["Diane","Demo"],
["Anthony","Kunovic"],
["Ellery","Buntel"],
["Emma","Buntel"],
["Taylor Webb","Frazier"],
["Mike","Nimer"]
]}
This doesn't look like the notation Angular used in their demo:
[
{text:'learn angular', done:true},
{text:'build an angular app', done:false}
]
Can someone point me to the right direction as to how I can go about getting this to work properly? Ideally, I would like to keep the CFC intact so that in can be reused for a different application so the JSON manipulation would have to be done in the Javascript end.
All CFCs automatically extend the ColdFusion WEB-INF/ cftags /component.cfc component. (The WEB-INF directory is in the cf_root / wwwroot directory on ColdFusion configured with an embedded J2EE server. It is in the cf_root directory when you deploy ColdFusion on a J2EE server.) This CFC is distributed as a zero-length file.
You use the Super keyword only on CFCs that use the Extends attribute to extend another CFC. Unlike ColdFusion scopes, the Super keyword is not used for variables; it is only used for CFC methods, and it is not available on ColdFusion pages that invoke CFCs.
(The WEB-INF directory is in the cf_root / wwwroot directory on ColdFusion configured with an embedded J2EE server. It is in the cf_root directory when you deploy ColdFusion on a J2EE server.) This CFC is distributed as a zero-length file.
If you place two components in a single directory as a package, and one component refers to the other with only the component name, not a qualified path, ColdFusion always searches the package directory first for the component.
Or you can use this helper function in javascript to get query as (common) key-value objects array.
function CFQueryParser(data) {
let items = [];
Object.keys(data.DATA).forEach((i) => {
let item = {};
Object.keys(data.COLUMNS).forEach((j) => {
item[data.COLUMNS[j]] = data.DATA[i][j];
});
items.push(item);
})
return items;
}
By default, Coldfusion uses a different JSON notation than you may be used to. The column names are stored in one array, while the data is stored in another. The solution we implemented involved changing the CFquery to an array. Then JSONEncoding that array.
You will need this function here:
<cffunction name="QueryToArray" access="public" returntype="array" output="false"hint="This turns a query into an array of structures.">
<cfargument name="Data" type="query" required="yes" />
<cfscript>
// Define the local scope.
var LOCAL = StructNew();
// Get the column names as an array.
LOCAL.Columns = ListToArray( ARGUMENTS.Data.ColumnList );
// Create an array that will hold the query equivalent.
LOCAL.QueryArray = ArrayNew( 1 );
// Loop over the query.
for (LOCAL.RowIndex = 1 ; LOCAL.RowIndex LTE ARGUMENTS.Data.RecordCount ; LOCAL.RowIndex = (LOCAL.RowIndex + 1)){
// Create a row structure.
LOCAL.Row = StructNew();
// Loop over the columns in this row.
for (LOCAL.ColumnIndex = 1 ; LOCAL.ColumnIndex LTE ArrayLen( LOCAL.Columns ) ; LOCAL.ColumnIndex = (LOCAL.ColumnIndex + 1)){
// Get a reference to the query column.
LOCAL.ColumnName = LOCAL.Columns[ LOCAL.ColumnIndex ];
// Store the query cell value into the struct by key.
LOCAL.Row[ LOCAL.ColumnName ] = ARGUMENTS.Data[ LOCAL.ColumnName ][ LOCAL.RowIndex ];
}
// Add the structure to the query array.
ArrayAppend( LOCAL.QueryArray, LOCAL.Row );
}
// Return the array equivalent.
return( LOCAL.QueryArray );
</cfscript>
</cffunction>
And then your return will look like:
<cfreturn SerializeJson(QueryToArray(getArtists_SQL),true)>
The thing to remember, is that a CFquery object contains other properties like recordcount...and most likely, the JS only wants the data. I don't know if there is a more elegant solution to this, but this is the solution we landed on when we were having a similar problem with JQgrid.
To go along with the above answer from Blaise. The queryToArray I use looks at the query object's columnList. This is so the case of the column aliases is preserved. Otherwise it will be all caps in your JSON
/**queryToArray
* utility method to keep the code dry.
* @hint does exactly what the name says, take a query, makes it an array of stucts
* @hint columnLabels pass in a list of columnLabels to just return those columns
*/
public array function queryToArray(required query data, any columnLabels=false){
var columns = listToArray(arguments.data.columnList);
if(arguments.columnLabels != false){
columns = listToArray(arguments.columnLabels);
}
var queryArray = arrayNew(1);
for(i=1; i <= arguments.data.RecordCount; i++){
row = StructNew();
for (j=1; j <= ArrayLen(columns); j++){
columnName = columns[j];
row[columnName] = arguments.data[columnName][i];
}
arrayAppend(queryArray, row);
}
return(queryArray);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With