Posted in jQuery, ColdFusion | Posted on 10-18-2009 | 4,234 views
I have to apologize for the title of this blog entry. I can't honestly think of a nicer way to say it. But enough of that, let me just get to the question.
I have searched all over the web and read countless articles and documentation, tried different examples but i just cant seem to figure out what i first thought was a relatively simple request and solution, maybe it is but i cant seem to figure it out, anyway enough of my waffling here's the scenario:
What i have is a very straight forward form with say 5 different text inputs. What i would like to do is on the first input box, which say is an invoice number, is for the user to enter the invoice number and then hit a button next to the input box and with the power of Coldfusion's built in AJAX stuff go to a cfc which has a query in it which searches for the invoice number, if one is found return the invoice number plus its details back to the form via AJAX, so all of the 5 text boxes would have returned data in them and the form isnt refreshed.
As with most things in ColdFusion, there are a couple ways of doing this. Let's start with one example and then I'll show a way to simplify it. First though, we need a simple form. I'll use the cfartgallery sample database for my example.
2id: <cfinput type="text" name="artid" id="artid"><br/>
3name: <cfinput type="text" name="artname" id="artname"><br/>
4description: <cftextarea name="description" id="description"></cftextarea><br/>
5price: <cfinput type="text" name="price" id="price"><br/>
6</cfform>
I've got a form with 4 inputs. The first one is our primary ID field. When the user enters data there, we then want to load the 3 other fields with the corresponding data. One way to do that would be with binding.
2id: <cfinput type="text" name="artid" id="artid"><br/>
3name: <cfinput type="text" name="artname" id="artname" bind="cfc:test.getName({artid@keyup})" readonly="true"><br/>
4description: <cftextarea name="description" id="description" bind="cfc:test.getDescription({artid@keyup})" readonly="true"></cftextarea><br/>
5price: <cfinput type="text" name="price" id="price" bind="cfc:test.getPrice({artid@keyup})" readonly="true"><br/>
6</cfform>
I've modified the 3 form fields now so that each of them is bound to the ID field. (I also added a readonly flag just to make it clear to the user that these fields are bound to the back end data.) For each field we run a differnent method: getName, getDescription, and getPrice. These are all bound to one CFC:
2
3<cffunction name="getData" access="remote">
4 <cfargument name="artid" required="true">
5 <cfset var q = "">
6
7 <cfquery name="q" datasource="cfartgallery">
8 select *
9 from art
10 where artid = <cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.artid#">
11 </cfquery>
12
13 <cfreturn q>
14</cffunction>
15
16<cffunction name="getDescription" access="remote">
17 <cfargument name="id" type="any">
18 <cfif not isNumeric(arguments.id) or arguments.id lte 0>
19 <cfreturn "">
20 </cfif>
21 <cfreturn getData(arguments.id).description>
22</cffunction>
23
24<cffunction name="getName" access="remote">
25 <cfargument name="id" type="any">
26 <cfif not isNumeric(arguments.id) or arguments.id lte 0>
27 <cfreturn "">
28 </cfif>
29 <cfreturn getData(arguments.id).artname>
30</cffunction>
31
32<cffunction name="getPrice" access="remote" returntype="string">
33 <cfargument name="id" type="any">
34 <cfif not isNumeric(arguments.id) or arguments.id lte 0>
35 <cfreturn "">
36 </cfif>
37 <cfreturn getData(arguments.id).price>
38</cffunction>
39
40</cfcomponent>
As you can see, the 3 methods all make use of a central getData() method. That method runs the query and then the individual queries all just return one field. (ColdFusion 9 users - please read my PS at the end.) This works, but as you can guess, each time we type into the ID field we perform 3 Ajax requests. This isn't horrible - and the data being returned is rather small, but multiple network requests for the same row of data could probably be done better. Let's look at a modified version.
2
3<script>
4
5function showData(d) {
6 //convert into a struct
7 var data = {}
8 for(var i=0; i < d.COLUMNS.length; i++) {
9 data[d.COLUMNS[i]] = d.DATA[0][i]
10 }
11 document.getElementById('artname').value = data["ARTNAME"]
12 document.getElementById('description').value = data["DESCRIPTION"]
13 document.getElementById('price').value = data["PRICE"]
14
15}
16</script>
17
18<cfform>
19id: <cfinput type="text" name="artid" id="artid"><br/>
20name: <cfinput type="text" name="artname" id="artname" readonly="true"><br/>
21description: <cftextarea name="description" id="description" readonly="true"></cftextarea><br/>
22price: <cfinput type="text" name="price" id="price" readonly="true"><br/>
23</cfform>
This code removes all the bindings from the fields. Instead we use cfajaxproxy to create a binding between the ID field, a CFC, and a JavaScript function. Whenever the ID field is changed, we run the getData method from the CFC and pass the results to our JavaScript function. Then the function just has to parse the result and set the form fields. Much easier, right? Now we have one Ajax request that fetches all the data. Technically we are passing back the same amount of data, but with only network request we will be less prone to suffer from congestion over the intertubes.
Oh, and because I couldn't help myself, I whipped up a quick jQuery version. I'll just paste the script blocks as that's the only thing that changed.
2<script>
3$(document).ready(function() {
4
5 $("#artid").keyup(function() {
6 var artid = $(this).val()
7 if(isNaN(artid)) return
8
9 $.getJSON("test.cfc?method=getdata&artid=" + artid + "&returnformat=json", {}, function(d,status) {
10 var data = {}
11 for(var i=0; i < d.COLUMNS.length; i++) {
12 data[d.COLUMNS[i]] = d.DATA[0][i]
13 }
14 $("#artname").val(data["ARTNAME"])
15 $("#description").val(data["DESCRIPTION"])
16 $("#price").val(data["PRICE"])
17 })
18
19 })
20})
21</script>
Enjoy!
PS: There is a fairly serious bug with ColdFusion 9 and returning JSON from CFCs. Before I post details though I'm waiting for the guy who found it to let me know if he has blogged it already. For now though note that the returnType="string" on getPrice is required in ColdFusion 9.


Many thanks for posting this example. I didn't expect to see an example posted so quickly.
This is really going to help me out and i appreciate your time taken to post these examples.
Also thank you for posting more than one variation, this also helps in understanding the best method to use.
From your example its seems so straight forward, i guess its true in what they say, knowledge is power :)
Many thanks again
Matt
Here's a sample code:
<cfhttp method="get" url="webservice.asmx/method?string=#form.variable#"></cfhttp>
<cfset Result = xmlparse(cfhttp.FileContent)>
<cfset xmlRoot = Result>
<cfset nodes = xmlSearch(xmlRoot, "//tbCEP")>
<cfif arrayLen(nodes)>
<cfoutput>#nodes[1].logradouro#</cfoutput>
Results: <br />
<cfoutput>
stType = #nodes[1].sttype# <br/>
stName = #nodes[1].name# <br/>
</cfoutput>
this would then need to go back to the form page and populate a form. I'm using the cfoutput just forn illustration...
I did that. I used QueryNew and tested the code several times... it worked fine until the Ajax part... the fields are populating, but they are populating with "undefined"...
Here is a snippet of my form page: http://i3.photobucket.com/albums/y95/abitdodgy/For...
And here is a snippet of my component page:
http://i3.photobucket.com/albums/y95/abitdodgy/Com...
I'm really not sure why it's returning undefined... :/
<cfif IsDefined("Form.GetInfo")>
<cfinvoke component="CheckCEP" method="ReturnAddress" returnvariable="q_AddressData">
<cfinvokeargument name="strcep" value="#form.strcep#">
</cfinvoke>
<cfdump var="#q_AddressData#">
</cfif>
And using a correct CEP actually returns a good result!
({COLUMNS: ["STTYPE", "STNAME", "AREA", "CITY", "STATE"], DATA: [["Rua", "Sao Benedito", "Santo Amaro", "Sao Paulo", "SP"]]});
2 /* !eval(new String("("+json+");)) */
The values you see under, well, they are correct. If I query 04735005, I do get the data shown above from the webservice... however, it's coming back to the fields as undefined for some reason! :/
First, notice my main loop. Let's see if it is doing anything.Before
for(var i=0; i < d.COLUMNS.length; i++) {
add
console.log("The length of cols is "+d.COLUMNS.length);
"Error invoking CFC test.cfc : Internal Server Error [enable debugging by adding 'cfdebug' to your URL parameters to see more information]"
actually im getting into CF coz i always was a php fan .. and i dont have a clue of what im doing wrong so if you can help me .. thnx a lot :D
c yas
Ok, so the best thing to do is use Firebug, or Chrome Dev tools, to inspect what was returned. Are you familiar with those tools?
http://imageshack.us/photo/my-images/823/unledgwi....
this is what im getting
http://imageshack.us/photo/my-images/685/13721870....
http://imageshack.us/photo/my-images/831/96437977....
http://imageshack.us/photo/my-images/195/72236196....
Btw, if this is online, I can do it for you.
here there're the screens (the correct ones)
http://imageshack.us/photo/my-images/98/26786048.p...
http://imageshack.us/photo/my-images/269/89995251....
http://imageshack.us/photo/my-images/705/40229919....
and no .. it's not online coz my boss says that Ajax leave a hole in the server security and im not allow to put ajax content online :/
Or - I think you can also right click in Chrome/Firebug and open the request in a new tab.
Either way - enable robust exception info and it will tell you the line # of your error and what it is.
Also, your boss is wrong. Completely. A AJAX request is no more or less secure than a non-AJAX request.
now :P
what is "arguments" in the code?
i see that you pass it in the getData function query and then you use it in the other function, but i dont see where it come from.
i just wanted to ask coz i removed the "arguments" from the test.cfc and now it works well, as long as my "artid" field is not null, when it's empty i get the same error that i told ya .. so is there any way to make it i dont know ... with a button or something ? :)
I'd restore the CFC code to match mine. Run it again and tell us what the error is exactly. If it complains that arguments artid doesn't exist, then you need to use Firebug/CHrome again to look at the XHR but this time focus on the Request, not the Response. If you don't see artid in there something is amiss. Ensure you didn't typo when copying the client portion of the code.
Again - if I could see this in action, it would be a lot quicker to debug. Maybe ask your boss if you can put it up someplace temporary and then email me directly.
[Add Comment] [Subscribe to Comments]