Posted in jQuery, ColdFusion | Posted on 02-12-2009 | 13,945 views
For my final (well, for now) post on jQuery and forms validation, I thought I'd actually create a real form with actual back end processing. I'm going to demonstrate with a form that makes use of both client-side and server-side validation, and also demonstrate one of the cooler features of the jQuery Validation library - remote validation.
Let's get started by describing our form and building it for entirely server-side validation. Imagine that I run a blog aggregator (oh wait, I do) and I want to make it easy for folks to send me information on their blogs so I can add it to the database. I'd need a form that asks for their blog name, URL, and RSS URL. (To be anal, I also use a description field at CFBloggers, but I'll keep it simple for now.) When not working within a framework like Model-Glue, I'll typically build a self-posting form (pseudo-code):
2
3notice a form submission:
4 create a list of errors
5 if no errors, email, save to db, etc, and push to thank you page
6
7display form:
8 optionally display errors
Here is the initial version of the form with ColdFusion performing the validation server side. I assume none of this is unusual and since and the focus here is on jQuery I won't go over the code.
2<cfparam name="form.blogurl" default="">
3<cfparam name="form.rssurl" default="">
4
5<cfif structKeyExists(form, "save")>
6 <cfset errors = []>
7 <cfif not len(trim(form.blogname))>
8 <cfset arrayAppend(errors, "You must include a blog name.")>
9 </cfif>
10 <cfif not len(trim(form.blogurl)) or not isValid("url", form.blogurl)>
11 <cfset arrayAppend(errors, "You must include a blog url.")>
12 </cfif>
13 <cfif not len(trim(form.rssurl)) or not isValid("url", form.rssurl)>
14 <cfset arrayAppend(errors, "You must include a rss url.")>
15 </cfif>
16 <cfif arrayLen(errors) is 0>
17 <cfmail to="ray@camdenfamily.com" from="ray@camdenfamily.com" subject="RSS Submission">
18Blog Name: #form.blogname#
19Blog URL: #form.blogurl#
20RSS URL: #form.rssurl#
21 </cfmail>
22 <cflocation url="rssaddthanks.cfm" addToken="false" />
23 </cfif>
24</cfif>
25
26<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
27<html xmlns="http://www.w3.org/1999/xhtml">
28<head>
29<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
30<title>Untitled Document</title>
31</head>
32
33<body>
34
35<h2>Add RSS Feed</h2>
36<form id="mainform" action="rssadd.cfm" method="post">
37<fieldset>
38
39<cfif structKeyExists(variables,"errors")>
40 <b>Please correct the following error(s):</b><br/>
41 <ul>
42 <cfloop index="e" array="#errors#">
43 <li><cfoutput>#e#</cfoutput></li>
44 </cfloop>
45 </ul>
46</cfif>
47
48<legend>Fill out the details of you blow below.</legend>
49<cfoutput>
50<p>
51<label for="blogname">Blog Name</label>
52<em>*</em><input id="blogname" name="blogname" size="25" value="#form.blogname#" />
53</p>
54<p>
55<label for="blogurl">Blog URL</label>
56<em>*</em><input id="blogurl" name="blogurl" size="25" value="#form.blogurl#" />
57</p>
58<p>
59<label for="rssurl">RSS URL</label>
60<em>*</em><input id="rssurl" name="rssurl" size="25" value="#form.rssurl#" />
61</p>
62</cfoutput>
63<p>
64<input class="submit" type="submit" name="save" value="Submit"/>
65</p>
66</fieldset>
67</form>
68
69</body>
70</html>
Alright, so nothing too scary in there, right? You can demo this online here.
Let's add some jQuery love to the page. I'll begin by including my libraries of course:
2<script src="/jquery/jquery.validate.js"></script>
Next I'll set up my validation and rules:
2 $("#mainform").validate({
3 rules: {
4 blogname: "required"
5 ,blogurl: "required url"
6 ,rssurl: "required url"
7 }
8
9 });
10});
The details of how this works are described in my last entry, but basically I'm saying that all 3 fields are required and blogurl and rssurl also need url validation. (Hey IE folks, did I do my commas right?)
Again, this just plain works. You can demo this here. If you disable JavaScript, you still get the server side validation. It took me about 30 seconds to add in the JS validation though so I don't mind writing it twice.
Alright, but now it's time to get sexy. jQuery's validation plugin comes in with a number of default rules you can use. I also demonstrated how you can write your own rules. Sometimes though there are things you want to do that are impossible with JavaScript. jQuery Validation supports a style of validation simply called 'remote'. By specifying a URL for a validation rule, the plugin will automatically run your URL (passing the field name and field value). Your server-side code does what it needs to and outputs either true or false. Let me demonstrate. First, I'll modify my rules declaration:
2 blogname: "required"
3 ,blogurl: {
4 required:true
5 ,url:true
6 ,remote:"rssprocess.cfm"
7 }
8 ,rssurl: {
9 required:true
10 ,url:true
11 ,remote:"rssprocess.cfm"
12 }
13}
So, in English, this means that:
The name value will be required.
The blogurl value will be required, must be a URL, and the value will be passed to rssprocess.cfm and it must return true.
The rssurl value will be required, must be a URL, and the value will be passed to rssprocess.cfm and it must return true.
I'm using the same file to process both requests. I can do this because the plugin will send the name of the field as well. I could have used two different CFMs, or even two different CFC methods. Let's look at rssprocess.cfm:
2
3<cfif structKeyExists(url, "blogurl")>
4
5 <!--- if blogurl, just do a check for status code 200 --->
6 <cfhttp url="#url.blogurl#" result="result">
7 <cfif structKeyExists(result.responseheader,"status_code") and result.responseheader.status_code is 200>
8 <cfoutput>true</cfoutput>
9 <cfelse>
10 <cfoutput>false</cfoutput>
11 </cfif>
12
13<cfelseif structKeyExists(url, "rssurl")>
14
15 <!--- if blogurl, just do a check for status code 200 --->
16 <cftry>
17 <cffeed source="#url.rssurl#" query="foo">
18 <cfoutput>true</cfoutput>
19 <cfcatch>
20 <cfoutput>false</cfoutput>
21 </cfcatch>
22 </cftry>
23<cfelse>
24 <cfoutput>false</cfoutput>
25</cfif>
I begin by turning on cfoutputonly. I'm not sure how well the plugin will handle values with whitespace around so it so I'm going to be anal about my output. I then check my URL scope. If blogurl was sent, I just do a HTTP check to ensure the URL exists. If rssurl was sent, I try to read it with cffeed and return true if the RSS feed can be parsed by CF. Notice that I return false in all error conditions, and if no value was passed at all. (Because people like me will run your site with Firebug, notice the Ajax requests, and try to run the file manually.)
You can demo this here. I also added custom messages. You can view source on the demo to see that. That's it. I don't think I'll write another form without jQuery validation in it!
Edit at 9:36AM CST Epic fail on my part. Thank you to Esmeralda for reminding me. I forgot to htmlEditFormat the form data to help prevent XSS type attacks. I normally do something like this in all my form checks:
2 <cfset arrayAppend(errors, "You must include a blog name.")>
3<cfelse>
4 <cfset form.blogname = htmlEditFormat(trim(form.blogname))>
5</cfif>
Note the use of both trim and htmlEditFormat. Anyway, I've added it to all 3 dems, and thank you again Esmeralda for the reminder!


var v = $("#myformid").validate({
submitHandler: function(form) {
$(form).ajaxSubmit(frm_options);
},
rules {rules here},
messages {messages here}
});
I also use the BlockUI plugin to give some user feedback while the form is submitting and any errors that are returned. If you want to see some code give me a shout.
http://www.chapel-studios.co.uk/jqvalidate/
Change the Show Error radio button to see the success and error messages.
The 'no JS' validation does NOT do the HTTP/RSS check. It DOES check for basic validation (must be a value and must be a url). I do NOT repeat the 'check URL for existence' and 'check RSS feed' for non-JS folks, but they still get some validation.
So - worst case - I get an email with proper formatted URLs that aren't pointing to a real domain or rss feed. I could add that logic if I wanted to though. I did not because I didn't want to repeat the network calls on the submission for people who have JS turned on. I could get around that by submitting elsewhere (via an Ajax submission), or simply using jQuery to add a quick flag ('if form.ivalidatedclientside=1 then dont repeat). But I didn't think that would be worth the effort.
Let me know if that doesn't make sense.
i wouldn't consider your server side validation enough for inputting to a db, and didn't want anyone trying to learn something about validation from your site to think it was.
im in a glass half empty state today.
thanks for the tutorial! Still having a hard time getting it to work though. Any chance you can take a peek here and see what i'm doing wrong?
http://www.getmura.com/forum/messages.cfm?threadid...
Thanks,
David
remote: {
url: "checkUser.cfm",
type: "post"
}
this is the following of what i have:
username: {
required: true, remote: "checkUser.cfm", type: "post"
}
and my checkUser.cfm is set to:
<cfsetting enablecfoutputonly="true">
<cfset userBean=application.userManager.readByUsername(url.username,"default")>
<cfif userBean.getIsNew()>
<cfoutput>false</cfoutput>
<cfelse>
<cfoutput>true</cfoutput>
</cfif>
username: {
required: true, remote: {url: "checkUser.cfm",type: "post"}
}
note the extra curly brackets around the remote properties/arguments
You can see exactly what i mean here: http://sbiwd3.kattare.com/go/get-involved/join-our...
Thanks guys,
David
Thanks again Ray,
David
http://sbiwd3.kattare.com/go/get-involved/join-our...
then try
http://sbiwd3.kattare.com/go/get-involved/join-our...
They return the same page, yet the second clearly shouldn't exist!
That is correct. I was wondering the same thing. I'll see if Matt Levine can confirm why that would be.
Thanks again,
David
If rather than having 'remote:' point to a cfm I want to have 'remote:' point to a function called "process()" within rss.cfc, how would I do that?
In other words, how can we use a cfc rather than a cfm?
Thanks in advance. I've been reading your blog a lot lately.
then change the process function access to remote
The function should return either true or false IIRC.
Is that any other way i can set more than one form/url variable in DATA?
data: {
username: function() {
return $("#username").val();
},
email: function(){
return $("#email").val();
}
}
I try to used this way but it seem not worked..
I also try to used firebug but have no idea how to used it.. will need some time to study how to used firebug too~ ><
Anyway.. thank for your reply, will try you suggestion later.
Regard
keong
This is my jquery:
$("#addForm").validate({
errorContainer: "#error",
rules: {
faculty: {
remote: "management_val.cfm"
}
},
messages: {
faculty: "x"
}
});
I've been looking at it in firebug and it doesn't seem to be sending anything...but I can't figure out why it's not sending anything. All the names are correct, jquery.validate and jquery are included, and it's in a $(document).ready(function).
Any insight would be great! :)
(I'm kind of a rookie with jquery)
(The form is used to add registered faculty to a department.)
Do you think that is problem?
Thank for your firebug video. Very usefully for me.
btw, do you have time take a look for my jquery form validation issue ?
http://forum.jquery.com/topic/jquery-form-validati...
I try to figure out this few days but no luck.
Anyway, thank for your time and have a nice day..
Regard
keong
Maybe you can try below code :
remote: {
url: "<cfoutput>#application.http#</cfoutput>app/excel_upload/act_check_manager_respond_time.cfm",
type: "post",
data: { //3
key: function() {
var date1 = $("#CFB_Date_Sub").val();
var date2 = $("#Date_Resolved").val();
var action_reason = $("#lastname").val();
if (action_reason == ''){
jsaction_reason_check = "Yes"
}else{
jsaction_reason_check = "No"
}
var jointValue = jsaction_reason_check + "|" + date1 + "|" + date2;
return jointValue ;
}
}//3
}
Currently my form action goes to a second 'processing.cfm' page for db insert. Do you think there is a way to get your method working with a separate processing page? Or do I just need to move my db insert script onto the form page and replace your cfmail script?
At least that is what I did and it is working with JavaScript enabled or disabled. So thank you!
[Add Comment] [Subscribe to Comments]