Returning Array from CFC to JavaScript
Copy link to clipboard
Copied
I am trying to write a javascript that talks to a CFC and I need the CFC to return an Array that I can read back into the javascript (as a js array) and then loop through that list.
So in the CFC I create an array something like:
<CFFUNCTION name="getCity" access="remote" returnType="array">
<CFSET var result = ArrayNew(2)>
<CFSET result[0][1] = 1>
<CFSET result[0][2] = “Vancouver”>
<CFSET result[0][1] = 2>
<CFSET result[0][2] = “Detroit”>
<CFSET result[0][1] = 3>
<CFSET result[0][2] = “Miami”>
<CFRETURN result>
</CFFUNCTION>
Then in the JS return function:
function returnCity(returnArray) {
for (var i=0; i<returnArray.length; i++) {
document.formname.cityID.options.value = returnArray[0];
document.formname.cityID.options.text = returnArray[1];
}
}
Copy link to clipboard
Copied
<cfwddx> and toScript() are two ways to get Cold Fusion Data into js. I don't know if js can handle 2D arrays, so I always to this with 2 1D arrays.
Here is a bit of a sample that shows using toScript() for something similar to what you are attempting.
Copy link to clipboard
Copied
Outside of Dan's option, you can check out an Ajax library or two (jQuery, MooTools, Prototype) for this type of functionality. These libraries greatly simplify the communication b/w JS and server-side scripts (i.e., CFML). You can use 'regular' JS to run your XMLHttpRequest (http://en.wikipedia.org/wiki/XMLHttpRequest) but it's a good bit more coding than using a JS-Ajax library. Regardless of your approach (Ajax library or standard JavaScript), the XMLHttpRequest object is the key to browser-server communication of this nature.
Also, your JavaScript won't understand a CF array with these tools/libraries but you can use CF's SerializeJSON function to return JSON (JavaScript Object Notation) rom your CF data or just return the data as XML, either of which your JS can parse, loop over, handle as an array (sort of), etc.
Copy link to clipboard
Copied
The <cfwddx...> tag is built to translate complex variables back and forth particularly with javascript.
<cfwddx action="cfml2js" input="#cfVar#" output="JSString" toplevelvariable="JSVarName">
In your code example that might look something like this.
CFML
<CFFUNCTION name="getCity" access="remote" returnType="array">
<CFSET var result = ArrayNew(2)>
<CFSET result[0][1] = 1>
<CFSET result[0][2] = “Vancouver”>
<CFSET result[0][1] = 2>
<CFSET result[0][2] = “Detroit”>
<CFSET result[0][1] = 3>
<CFSET result[0][2] = “Miami”>
<cfwddx action="cfml2js" input="#result#" output="returnString" topLevelVariable="returnArray">
<CFRETURN returnstring>
</CFFUNCTION>
JavaScript
function returnCity(returnArray) {
for (var i=0; i<returnArray.length; i++) {
document.formname.cityID.options.value = returnArray[0];
document.formname.cityID.options.text = returnArray[1];
}
}
But I have never used this in a AJAX envornment. In this you may have to create the wddx package in the CFML and decode this package in the JavaScript. oYou would then use the "cfml2wddx" action. And you would have to include the JS wddx package to decode it. The documentation explains how to do all of that.
Copy link to clipboard
Copied
Well, I've tried about a million things and it isn't working. Currently I've been trying to follow the example on Adobe's site here:
http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=Tags_a-b_3.html
Here is what I have (I am in ColdFusion 8 Enterprise):
Here is my current CFC:
<CFCOMPONENT output="no">
<!--- Return a verified member list of states/provinces --->
<CFFUNCTION name="getStateVerMem" access="remote" returnFormat="json">
<CFQUERY datasource="dsnname" name="getSt">
SELECT stateID, stateAB FROM States
</CFQUERY>
<CFRETURN getSt>
</CFFUNCTION>
</CFCOMPONENT>
Here is my JS code:
<!--- Set AJAX Proxy --->
<CFAJAXPROXY cfc="cfc.event" jsclassname="evtreg">
<!--- Begin JavaScript processing ---->
<script language="javascript">
<!--
function returnMembership() {
var fieldInput = new evtreg();
fieldInput.setCallbackHandler(returnMemVerState);
fieldInput.setErrorHandler(myErrorHandler);
fieldInput.validateMembership();
}
// Update the verified member state
function returnMemVerState(returnString) {
for (var i=0; i<returnString.DATA.length; i++) {
document.formname.stateID.options.value = returnString.DATA[returnString.COLUMNS.findIdx(‘STATEID’)];
document.formename.stateID.options.text = returnString.DATA[returnString.COLUMNS.findIdx(‘STATEAB’)];
}
}
// Error handler for the asynchronous functions.
function myErrorHandler(statusCode, statusMsg){
alert('We are sorry, an error occured on this form.' + statusCode + ', ' + statusMsg);
}
-->
</script>
When the function returnMembership is called this should return data and populate the dropdown, but I keep getting an error: “DATA.length is null or not an object
Copy link to clipboard
Copied
If I run the CFC in a web browser I get the following output:
{"COLUMNS":["STATEID","STATEAB"],"DATA":[[2,"AK"],[1,"AL"],[4,"AR"],[3,"AZ"],[5,"CA"],[6,"CO"],[7,"CT"],[48,"DC"],[8,"DE"],[9,"FL"],[10,"GA"],[11,"HI"],[15,"IA"],[12,"ID"],[13,"IL"],[14,"IN"],[16,"KS"],[17,"KY"],[18,"LA"],[21,"MA"],[20,"MD"],[19,"ME"],[22,"MI"],[23,"MN"],[25,"MO"],[24,"MS"],[26,"MT"],[33,"NC"],[34,"ND"],[27,"NE"],[29,"NH"],[30,"NJ"],[31,"NM"],[28,"NV"],[32,"NY"],[35,"OH"],[36,"OK"],[37,"OR"],[38,"PA"],[39,"RI"],[40,"SC"],[41,"SD"],[42,"TN"],[43,"TX"],[44,"UT"],[46,"VA"],[45,"VT"],[47,"WA"],[50,"WI"],[49,"WV"],[51,"WY"]]}
Copy link to clipboard
Copied
I don't use cfajaxproxy but with all Ajax calls I make, I retuned JSON. Your CFC method is returning a query. CF has a function called SerializeJSON (http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=functions_s_03.html). You can pass it a query and have it convert that (or a struct, string, boolean, etc.) it to JSON.
Chang e in bold:
<CFCOMPONENT output="no">
<!--- Return a verified member list of states/provinces --->
<CFFUNCTION name="getStateVerMem" access="remote" returnFormat="json">
<CFQUERY datasource="dsnname" name="getSt">
SELECT stateID, stateAB FROM States
</CFQUERY>
<CFRETURN SerializeJSON( getSt )>
</CFFUNCTION>
</CFCOMPONENT>
Perhaps this conversion is something cfajaxproxy does but whenever my function/method needs to return JSON (didn't see any indication of this while skimming the docs), I convert my CF data types to JSON before the return.
What happens if you add alert( returnString ); to the first line of the returnMemVerState() function? Dos it read [Object object], show a string, etc.?
Copy link to clipboard
Copied
With CF8 you can just tell it returnFormat="json" which does the same as the SerializeJSON (see my output above from the CFC).
I did run the alert(returnString) and received [Object object] as my response.
Copy link to clipboard
Copied
Cool. Didn't know if that was one of CF's auto-magic conversions (I use CF8 but not those tags/features).
You might want to try this JavaScriot function: dump()
Code here: http://www.openjs.com/scripts/others/dump_function_php_print_r.php
It'll basically dump a JS object and you should get a better view of what's coming bak to you.
Here's one inspired by cfdump:
http://www.netgrow.com.au/files/javascript_dump.cfm
Finally, do you have Firefox & Firebug? If so, Firebug shows you the raw data returned from your Ajax calls. It can be very, very helpful!
Also, keep in mind JS is case-sensitive, so returnString.data is different than returnString.DATA. I've burned myself on that one plenty of times!!
Copy link to clipboard
Copied
Wierd, when I dump I get the following:
object | |
---|---|
MYRETURNVAL [number] | 0 |
Shouldn't that be the same as the output I receive when I run the CFC manually?
Copy link to clipboard
Copied
That's what I would expect, too. Or, at least an object-based representation of the CF-generated JSON. It would seem that CF isn't returning anything to the JS function but that it's creating the correct JSON.
It doesn't seem likely but do you need to add the onSuccess attribute to
<CFAJAXPROXY cfc="cfc.event" jsclassname="evtreg">
I know your setting stuff herer
function returnMembership() {
var fieldInput = new evtreg();
fieldInput.setCallbackHandler(returnMemVerState);
fieldInput.setErrorHandler(myErrorHandler);
fieldInput.validateMembership();
}
But I wonder if it would make a difference if you went the route of
<CFAJAXPROXY cfc="cfc.event" onSucces="returnMemVerState" onError="myErrorHandler">
You shouldn't have to change your JS at all for that test.
Copy link to clipboard
Copied
Well, the issue is that I am running other function calls with the code that are returning correct results. Just this one is being screwy for some reason.
Maybe I'll try isolating this call onto it's own CFAJAXPROXY. I haven't tried that before.
Thanks for your great help so far.
Copy link to clipboard
Copied
Well, you can't add those props into the cfajaxproxy this way as it causes this error:
Error in custom script module
(/customcf/event_registration.cfm)
Invalid attribute. The attributes onsuccess or onerror cannot be used with the CFC attribute.
I am just stumped as to why the output works manually, but it won't return it to my script. I'll have to try and remove everything else and isolate just this piece.
Copy link to clipboard
Copied
I wanted to make sure the dump functions were working, so I just ran this code (first dump function) and it dumped he data correctly
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Untitled Document</title>
<script type="text/javascript" src="lib/dump/dump.js"></script>
<!-- <script type="text/javascript" src="lib/dump/cf_dump.js"></script> -->
<script type="text/javascript">
function init() {
var assoc = {"COLUMNS":["STATEID","STATEAB"],"DATA":[[2,"AK"],[1,"AL"],[4,"AR"],[3,"AZ"],[5, "CA"],[6,"CO"],[7,"CT"],[48,"DC"],[8,"DE"],[9,"FL"],[10,"GA"],[11,"HI"],[15,"IA" ],[12,"ID"],[13,"IL"],[14,"IN"],[16,"KS"],[17,"KY"],[18,"LA"],[21,"MA"],[20,"MD" ],[19,"ME"],[22,"MI"],[23,"MN"],[25,"MO"],[24,"MS"],[26,"MT"],[33,"NC"],[34,"ND" ],[27,"NE"],[29,"NH"],[30,"NJ"],[31,"NM"],[28,"NV"],[32,"NY"],[35,"OH"],[36,"OK" ],[37,"OR"],[38,"PA"],[39,"RI"],[40,"SC"],[41,"SD"],[42,"TN"],[43,"TX"],[44,"UT" ],[46,"VA"],[45,"VT"],[47,"WA"],[50,"WI"],[49,"WV"],[51,"WY"]]};
alert(dump(assoc));
}
window.onload=init;
</script>
</head>
<body>
</body>
</html>
I then ran this code (cfdump-inspired) function:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Untitled Document</title>
<!-- <script type="text/javascript" src="lib/dump/dump.js"></script> -->
<script type="text/javascript" src="lib/dump/cf_dump.js"></script>
<script type="text/javascript">
function init() {
var assoc = {"COLUMNS":["STATEID","STATEAB"],"DATA":[[2,"AK"],[1,"AL"],[4,"AR"],[3,"AZ"],[5, "CA"],[6,"CO"],[7,"CT"],[48,"DC"],[8,"DE"],[9,"FL"],[10,"GA"],[11,"HI"],[15,"IA" ],[12,"ID"],[13,"IL"],[14,"IN"],[16,"KS"],[17,"KY"],[18,"LA"],[21,"MA"],[20,"MD" ],[19,"ME"],[22,"MI"],[23,"MN"],[25,"MO"],[24,"MS"],[26,"MT"],[33,"NC"],[34,"ND" ],[27,"NE"],[29,"NH"],[30,"NJ"],[31,"NM"],[28,"NV"],[32,"NY"],[35,"OH"],[36,"OK" ],[37,"OR"],[38,"PA"],[39,"RI"],[40,"SC"],[41,"SD"],[42,"TN"],[43,"TX"],[44,"UT" ],[46,"VA"],[45,"VT"],[47,"WA"],[50,"WI"],[49,"WV"],[51,"WY"]]};
//alert(dump(assoc));
dump( assoc, true );
}
//window.onload=init;
</script>
</head>
<body>
<a href="#" onclick="init()">dump</a>
</body>
</html>
This also showed me the JSON data in the CFDUMP way I expected.
In both cases, the included JS files only contain the previously linked-to dump functions. It seems rather odd that CF isn't returning the correct data. Sorry I don't know more about cfajaxproxy!
Message was edited by: craigkaminsky -- took out single quotes surrounded assoc var to get correct dump output

