Question

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.

Was it helpful?

Solution

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.

OTHER TIPS

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

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


Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top