Last week I blogged a very simple example of jQuery to ColdFusion communication. At the request of a reader I created the simplest example I could come up with that demonstrated the basic concepts. Today I have a slightly more advanced example, one that specifically makes use of a form and a post operation.
My example is a very simple authentication system. The form has a username and password field. We want to integrate with a ColdFusion Component that will handle responding to the post from jQuery. Let's begin with the front end template.
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script>
$(document).ready(function() {
//grab the submit action
$("#loginForm").submit(function(e) { //stop the form submission
e.preventDefault() //get my values
var uval = $("#username").val()
var pval = $("#password").val() //basic validation
if(uval == '' || pval == '') return //disable the button
$("#loginButton").attr("disabled",true) //Let the user know we are doing something
$("#result").html("Logging in...") //Send them to the CFC
$.post("test.cfc?method=authenticate&returnformat=json",
{username:uval, password:pval},
function(res) {
//Handle the result
if(res == "true") {
$("#result").html("Your login worked.")
//Do more here.
} else {
$("#result").html("Your login failed.")
$("#loginButton").removeAttr("disabled")
}
}) })
})
</script>
</head> <body> <form id="loginForm">
username: <input type="text" name="username" id="username"><br/>
password: <input type="password" name="password" id="password"><br/>
<input type="submit" value="Login" id="loginButton">
</form> <div id="result"></div> </body>
</html>
<html>
Ok, we've got a few things to cover here. I'll begin with the HTML at the bottom. You can see my simple form (2 text fields and a submit button). Also notice the little "result" div at the bottom. I'll be using that later to provide feedback to the user.
Ok, now scroll up to the JavaScript. The meat of this template is all within one main event handler defined here:
$("#loginForm").submit(function(e) {
This is going to "take over" the normal form submission and let me do something else with it. Notice too that the very first command within the handler is: e.preventDefault(). This will ensure that my form never does actually submit (if the user has JavaScript enabled of course). Moving down, I grab the value from my two fields. I do this manually but jQuery does provide a few ways of serializing a form all at once. Once I've got that, I do some very simple validation. If either field is blank we leave the function.
Next up I disable the submit button. Remember that we're going to be doing an Ajax post, a network operation, and that isn't instantaneous. Disabling the submit button is an easy way to prevent a user from going click happy. We also add a status message so that the user knows something is going on.
The portion that actually performs the Ajax based request begins with $.post. We provide the URL first. Remember that you must pass the method to execute within the CFC. That's the method=authenticate part. You can - and normally should - provide a returnFormat argument as well to tell ColdFusion how to format the response. The second argument is a structure of data. These will be sent as POST fields to the CFC. Lastly we have a response handler. This is going to execute when the CFC returns a result to us. For our simple example we are assuming a string result of "true" or "false". Obviously there may be more complex results. In a longer form, you may have a set of error messages. In the result we either provide a "good" message or a "bad" message. As you can see in the comments, you would probably do a bit more on a good result. You may want to hide the form for example. Or you may actually push the user to another URL.
Now let's take a look at the CFC:
remote boolean function authenticate(string username, string password) {
sleep(1400);
if(arguments.username == "paris" && arguments.password == "hilton") return true;
return false;
} }
component {
Yeah, I love me some script-based CFCs. So as you can imagine, this would probably be replaced with a query call or perhaps LDAP. But that doesn't really matter here. I added a sleep command in there to help simulate a slow network. To see this yourself, click the big new fancy Demo button below.
Archived Comments
Thanks Ray another great example. As a beginner with jQuery it's so helpfull to see working examples with jQuery and coldfusion.
Where's the link to the demo? (Kidding!!!)
Waiting for more examples! This is a very good jump start!
You could possibly clean it up a bit by moving everything from the get to the post:
[code]
$.post("test.cfc",
{'method':'authenticate','returnformat':'returnformat','username':uval, 'password':pval},
function(res) {
//Handle the result
if(res == "true") {
$("#result").html("Your login worked.")
//Do more here.
} else {
$("#result").html("Your login failed.")
$("#loginButton").removeAttr("disabled")
}
})
[/code]
Also I'd recommend putting quotes aroung username and password in the json object for clarity, as they look exavtly like variables without them.
I prefer the 'meta' stuff to be in the url, namely the method and returnformat. I _can_ say it has bugged me for sometime now the way my objects "looked" - I didn't know I could do:
"key":value
That's much better. Thanks!
One of the reasons to use 'post' instead of 'get' is that it acts as a deterrent against XSS (Cross Site Scripting) attacks. It's not a fail-safe however.
I've never heard that POST helps prevent XSS. Do you have a URL to back that up? I do know it allows you to send more data though.
http://www.technicalinfo.ne...
Interesting. Their argument is that it is harder to hack forms. I disagree with this. Well, it _is_ harder, much like it is harder for me to take candy from a 4 year old versus a 2 year old, but I'm not sure I'd even recommend this as it may give people a false sense of security. It is _trivial_ to hack a form. Is it as trivial as a URL hack? No. But it will take an attacker all of 2-3 more minutes. Even less with the right Firefox extension.
I should add - I'm no security expert. So take the previous comment as my opinion only. :)
Ya, it wouldn't do anything to deter the kind of hackers you would need to worry about. I'm definitely not claiming to be a security expert either :) I guess just a tidbit to think about.
One possible advantage to having the metadata in the get portion of the url would be for bookmarking, however that's irrelevant when referring to ajax requests of course.
I've also read that using get instead of post, the data in the url gets logged, such as Apache logs, etc. I guess that could be an advantage or disadvantage depending on what you want, and the situation.
One more disadvantage of POST - the network traffic is higher. This was mentioned in the Client Side performance session at cfObjective.
Then again, POST is the _only_ safe way to send a lot of data. So if you are posting a form with a textarea, you pretty much _have_ to use POST.
Very handy with a onKeyUp expression. The user just typed the last char of his password and whom. logged in. without pressing a submit button or something else :)
How might you clear the username and password fields after successful logon?
Give u/p fields with IDs of "username" and "password":
$("#username").val("")
$("#password").val("")
And that would go after
$("#result").html("Your login worked.")
Correct?
Doesn't seem to be working, any ideas?
Did you use a username field with an ID value of username? Ditto for password? Is it online where we can see?
Ahhh, doesn't work in Chrome, but does in FF.
Can you share the URL?
No, it's not online and I did use the id's correctly. It's just an issue with Chrome. Works in both IE and FF.
Well, in general, Chrome is darn good - so I'm sure it can work. Can you share the source perhaps? (Don't paste here - use pastebin.)
Honestly, it's your exact code with my 2 additions for clearing the form. I agree with you about Chrome.
boggle - then you got me. :) I've got no idea why it would work fine in IE/FF but not Chrome. Normally it's "Works fine in FF/CHrome and not IE". I use Chrome 100% of the time now and don't see this.
Just tested in Windows/Chrome/5.X and it worked fine. Maybe open up the dev tools in Chrome and see if you see an error?
Sorry Ray, Chrome was caching something. Must be Monday morning. :)
So I used this with a regular ol' component in my cfc, not a script based like you have and the return wouldn't work properly. I posted a question on stack overflow and someone said to use output="false" on the entire cfc to remove the white space in my return from the component. It worked like a charm.
I sort of narrowed it down by alerting the response and saw it had a wild amount of white space. My initial fix was to use res.trim() == ... and that worked.
My question is, if I set my entire cfc to output="false" to accommodate this particular function what else will that screw up? I've got all my queries and other logic in the cfc and I'm wanting to avoid any ghost errors.
Nothing at all else will screw up - unless you actually try to output from the CFC itself, which most folks never actually do.
So short answer - don't worry about it. I _always_ use output=false on my cfcomponent and cffunction tags. (Well, "always" being before I switched to ColdFusion 9.)
Raymond,
Your CF blog is the best. Most understandable, most applicable, and easiest to follow. Thanks for this post.
Bill
Wow, very nice words. Thanks Bill.