An Introduction to jQuery and Form Validation (3)

This post is more than 2 years old.

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):

default form value

notice a form submission: create a list of errors if no errors, email, save to db, etc, and push to thank you page

display form: 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.

<cfparam name="form.blogname" default=""> <cfparam name="form.blogurl" default=""> <cfparam name="form.rssurl" default="">

<cfif structKeyExists(form, "save")> <cfset errors = []> <cfif not len(trim(form.blogname))> <cfset arrayAppend(errors, "You must include a blog name.")> </cfif> <cfif not len(trim(form.blogurl)) or not isValid("url", form.blogurl)> <cfset arrayAppend(errors, "You must include a blog url.")> </cfif> <cfif not len(trim(form.rssurl)) or not isValid("url", form.rssurl)> <cfset arrayAppend(errors, "You must include a rss url.")> </cfif> <cfif arrayLen(errors) is 0> <cfmail to="ray@camdenfamily.com" from="ray@camdenfamily.com" subject="RSS Submission"> Blog Name: #form.blogname# Blog URL: #form.blogurl# RSS URL: #form.rssurl# </cfmail> <cflocation url="rssaddthanks.cfm" addToken="false" /> </cfif> </cfif>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Untitled Document</title> </head>

<body>

<h2>Add RSS Feed</h2> <form id="mainform" action="rssadd.cfm" method="post"> <fieldset>

<cfif structKeyExists(variables,"errors")> <b>Please correct the following error(s):</b><br/> <ul> <cfloop index="e" array="#errors#"> <li><cfoutput>#e#</cfoutput></li> </cfloop> </ul> </cfif>

<legend>Fill out the details of you blow below.</legend> <cfoutput> <p> <label for="blogname">Blog Name</label> <em></em><input id="blogname" name="blogname" size="25" value="#form.blogname#" /> </p> <p> <label for="blogurl">Blog URL</label> <em></em><input id="blogurl" name="blogurl" size="25" value="#form.blogurl#" /> </p> <p> <label for="rssurl">RSS URL</label> <em>*</em><input id="rssurl" name="rssurl" size="25" value="#form.rssurl#" /> </p> </cfoutput> <p> <input class="submit" type="submit" name="save" value="Submit"/> </p> </fieldset> </form>

</body> </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:

<script src="/jquery/jquery.js"></script> <script src="/jquery/jquery.validate.js"></script>

Next I'll set up my validation and rules:

$(document).ready(function(){ $("#mainform").validate({ rules: { blogname: "required" ,blogurl: "required url" ,rssurl: "required url" }
});

});

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:

rules: { blogname: "required" ,blogurl: { required:true ,url:true ,remote:"rssprocess.cfm" } ,rssurl: { required:true ,url:true ,remote:"rssprocess.cfm" } }

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:

<cfsetting enablecfoutputonly="true">

<cfif structKeyExists(url, "blogurl")>

&lt;!--- if blogurl, just do a check for status code 200 ---&gt;
&lt;cfhttp url="#url.blogurl#" result="result"&gt;
&lt;cfif structKeyExists(result.responseheader,"status_code") and result.responseheader.status_code is 200&gt;
	&lt;cfoutput&gt;true&lt;/cfoutput&gt;
&lt;cfelse&gt;
	&lt;cfoutput&gt;false&lt;/cfoutput&gt;
&lt;/cfif&gt;

<cfelseif structKeyExists(url, "rssurl")>

&lt;!--- if blogurl, just do a check for status code 200 ---&gt;
&lt;cftry&gt;
	&lt;cffeed source="#url.rssurl#" query="foo"&gt;
	&lt;cfoutput&gt;true&lt;/cfoutput&gt;
	&lt;cfcatch&gt;
		&lt;cfoutput&gt;false&lt;/cfoutput&gt;
	&lt;/cfcatch&gt;
&lt;/cftry&gt;

<cfelse> <cfoutput>false</cfoutput> </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:

<cfif not len(trim(form.blogname))> <cfset arrayAppend(errors, "You must include a blog name.")> <cfelse> <cfset form.blogname = htmlEditFormat(trim(form.blogname))> </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!

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Dave Phipps posted on 2/12/2009 at 8:12 PM

You should now take a look at the jquery form plugin (http://snipurl.com/br5hq). I had some issues initially linking this plugin with the validation plugin and went for using the validationaide plugin instead, but after reading your last couple of entries and having got a form working correctly I am converted! By the way, here is a snippet of how to get the form plugin and validate plugin working together (in $(document).ready()):

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.

Comment 2 by Raymond Camden posted on 2/12/2009 at 8:14 PM

@David Phipps - I'd like to see that - can you post the URL to a sample online?

Comment 3 by Dave Phipps posted on 2/13/2009 at 12:10 AM

Here's a very rough demo:

http://www.chapel-studios.c...

Change the Show Error radio button to see the success and error messages.

Comment 4 by Raymond Camden posted on 2/13/2009 at 12:45 AM

Nice. Thanks for sharing that.

Comment 5 by shag posted on 2/13/2009 at 9:07 PM

@ray, maybe i'm reading something wrong, but what are you calling your back end validation? perhaps i should ask would you consider back end and server side the same? i guess i am missing where you actually validate when the data posted from the/any form is actually validated. i see where you call the remote process that validates the uri is working, but if i'm not mistaken, that is before the form is submitted? what's to stop someone from creating their own form and submitting it, or stopping the transaction after form submission and changing what is sent after your client/server validation calls.

Comment 6 by Raymond Camden posted on 2/13/2009 at 9:16 PM

Shag - the back end validation is two fold. One, we have basic back end stuff for when JS is turned off. Do you see that in the very first code sample? The second style is specifically to be used with jQuery and the Validation plugin. That's the last few code samples.

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.

Comment 7 by shag posted on 2/13/2009 at 9:50 PM

@ray, i was thinking more in terms of db input rather than mail. i would have been much more restrictive regarding the blog name regarding how long it could be, special characters... etc. i tend to think of server side validation as protecting from the bad stuff, and maybe this particular example is more checking to make sure the form is filled out. with less concern on is the data that is going to enter the application going to be destructive.

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.

Comment 8 by Raymond Camden posted on 2/13/2009 at 9:57 PM

Err, well, everyone has different requirements for their logic. My point here was to demonstrate both client side and server side doing _something_. What CF does is to ensure the value is not empty and that, for the URLs, the pattern is right. Obviously a person could have more 'rules' for their logic. I do not agree that my code is not 'enough', especially in regards to the main point of this blog entry. Then again, I'm probably being a bit defensive. (I'll admit to that.)

Comment 9 by David posted on 4/19/2010 at 8:58 AM

Hi Ray,
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/foru...

Thanks,
David

Comment 10 by Dave Phipps posted on 4/19/2010 at 12:33 PM

Try changing the remote rule to something like this:

remote: {
url: "checkUser.cfm",
type: "post"
}

Comment 11 by Raymond Camden posted on 4/19/2010 at 7:02 PM

@David - did Dave Phipps' tip help?

Comment 12 by David posted on 4/19/2010 at 7:15 PM

Still won't work.

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>

Comment 13 by Raymond Camden posted on 4/19/2010 at 7:33 PM

So what does Firebug tell you? Or the Chrome developer tools. Do you see the network request? What is the response?

Comment 14 by Dave Phipps posted on 4/19/2010 at 7:35 PM

Try changing your rule to look like this:

username: {
required: true, remote: {url: "checkUser.cfm",type: "post"}
}
note the extra curly brackets around the remote properties/arguments

Comment 15 by David posted on 4/19/2010 at 8:02 PM

ok so i've got some requests going in and out via firebug. Sorry Ray, still really new to Jquery :). Seems that things are working, but intermittently. If i type in a password that is already taken or not, the first time i type in anything in the username, it does nothing. However, once i change that value, then it seems the validation starts kicking in. However, the message will always stay even when the username is good.

You can see exactly what i mean here: http://sbiwd3.kattare.com/g...

Thanks guys,
David

Comment 16 by Raymond Camden posted on 4/19/2010 at 8:12 PM

Can you tell me a value for password that is good and one that is bad?

Comment 17 by Dave Phipps posted on 4/19/2010 at 8:16 PM

in addition to what Ray just asked, checkUser.cfm seems to be returning a full html page. It should just return true or false right?

Comment 18 by David posted on 4/19/2010 at 8:17 PM

By Password i'm assuming you mean username? :) one that is good is test and one that is bad is tests (ie. test is a valid username that should be taken). Do you happen to have a messenger that we can just chat on for a few minutes if you have time? If so, just email me it, otherwise we can just continue dialogue here.

Thanks again Ray,
David

Comment 19 by Dave Phipps posted on 4/19/2010 at 8:18 PM

In fact something weird is happening try going to checkUser.cfm directly:

http://sbiwd3.kattare.com/g...
then try
http://sbiwd3.kattare.com/g...

They return the same page, yet the second clearly shouldn't exist!

Comment 20 by David posted on 4/19/2010 at 8:18 PM

@Dave,
That is correct. I was wondering the same thing. I'll see if Matt Levine can confirm why that would be.

Comment 21 by Raymond Camden posted on 4/19/2010 at 8:19 PM

Dave got it - your checkUser.cfm is returning an entire page.

Comment 22 by Raymond Camden posted on 4/19/2010 at 8:20 PM

onMissingTemplate maybe?

Comment 23 by David posted on 4/19/2010 at 8:21 PM

Yup.. not sure why that is, but i'm assuming its something to do with Mura. It seems to return the page that i'm on.

Comment 24 by David posted on 4/19/2010 at 8:28 PM

BINGO!!!! Thanks guys!! Figured it out. I had to move the checkUser.cfm out of a particular directory and then set a path to it. Works like a charm now!
Thanks again,
David

Comment 25 by Neal posted on 5/10/2010 at 8:15 PM

Hi Ray,
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.

Comment 26 by Dave Phipps posted on 5/10/2010 at 8:20 PM

You just need to point to rss.cfc?method=process

then change the process function access to remote

The function should return either true or false IIRC.

Comment 27 by Neal posted on 5/10/2010 at 8:28 PM

Perfect, thanks Dave.

Comment 28 by keong posted on 6/1/2011 at 2:34 PM

The remote attribute = data: , it only can pass one value, I need to used a long list when i want to pass in more than one value, then split it via server scrip.

Is that any other way i can set more than one form/url variable in DATA?

Comment 29 by Dave Phipps posted on 6/1/2011 at 2:46 PM

@keong Just pass data as an object:

data: {
username: function() {
return $("#username").val();
},
email: function(){
return $("#email").val();
}
}

Comment 30 by keong posted on 6/2/2011 at 5:21 AM

Hi Dave

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

Comment 31 by Raymond Camden posted on 6/2/2011 at 5:25 AM

Video on how to use Firebug: http://www.developria.com/2...

Comment 32 by Bri Garrett posted on 6/10/2011 at 8:13 PM

So, I've been trying desperately to get this to work...

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)

Comment 33 by Bri Garrett posted on 6/10/2011 at 10:10 PM

Update: I tested it on a normal form and it works, so I think the fact that I have the field using autosuggest might be what's conflicting. It's bound to a query that identifies valid names for the field...
(The form is used to add registered faculty to a department.)

Do you think that is problem?

Comment 34 by Raymond Camden posted on 6/11/2011 at 1:37 AM

Possibly. Is it online where we can see?

Comment 35 by keong posted on 6/15/2011 at 1:52 PM

Hi Raymond

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/top...

I try to figure out this few days but no luck.

Anyway, thank for your time and have a nice day..

Regard
keong

Comment 36 by keong posted on 6/16/2011 at 2:08 PM

Bri Garrett

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
}

Comment 37 by dmitry posted on 8/2/2011 at 1:37 AM

If you have a form with lots of form elements, it seems like double work asking a user "Please Select Credit Card Type" in both coldfusion and jquery. I wonder if it's possible to fuse jquery validation into coldfusion logic so as to avoid repeating yourself?

Comment 38 by Raymond Camden posted on 8/2/2011 at 1:41 AM

cfform does that a bit (it's not jquery though) but at the end of the day, you're going to be doing both. I think it's ok though to have "good" errors in jQuery, and "ugly" errors server side. Ie, if you want to demand JavaScript support, you can/should still validate server side, but you can have simpler uglier output.

Comment 39 by Matthew posted on 10/26/2011 at 7:51 PM

I used jQuery Validation for a form with the silly assumption nobody would have javascript disabled. I like your method and want to implement it in my form which saves to a database.
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?

Comment 40 by Raymond Camden posted on 10/27/2011 at 4:53 AM

"Do you think there is a way to get your method working with a separate processing page? " Errr... well, the whole point of this is for _client_ side validation. It's easy to do validation server side with CF, but it's a different topic really.

Comment 41 by Matthew posted on 10/27/2011 at 4:34 PM

I guess my question should have been, "In your example how would you add that form data to a database once it has passed the client side validation?"

Comment 42 by Raymond Camden posted on 10/27/2011 at 5:01 PM

Once client side validation is done, the form submits as normal. So at that point... it's just a regular form post to ColdFusion. Makes sense, right?

Comment 43 by Matthew posted on 10/28/2011 at 12:08 AM

I'm obviously missing something because the form would not submit as normal. The form action takes the client back to that form page where it checks if there are no errors, then sends an email and redirects to the thanks page. But that redirect doesn't pass any form variables. So any db insert would have to take place within the cfif where the email is sent. Right?

At least that is what I did and it is working with JavaScript enabled or disabled. So thank you!

Comment 44 by Raymond Camden posted on 10/28/2011 at 1:51 AM

A cflocation can't pass form vars - you are right. Why not do the db insert before the redirect?

Comment 45 by Chris Bowyer posted on 4/5/2014 at 4:35 PM

Very cool! Never knew about the remote method before. Only issue is, it doesn't work for optional fields - even if no length is true e.g.

<cfif structKeyExists(url, "telephone")>
<cfif not len(url.telephone) or reFind("^[0][2378]\d{8}$", url.telephone)>
<cfoutput>true</cfoutput>
<cfelse>
<cfoutput>false</cfoutput>
</cfif>
</cfif>

Would love to be able to get around this. Can you or anyone else help?

Comment 46 by Raymond Camden posted on 4/5/2014 at 4:40 PM

I'm confused - so you are saying that if the field is optional, but you are using remote logic to validate it, the remote check never fires?

Comment 47 by Chris Bowyer posted on 4/5/2014 at 6:11 PM

If required is true, everything fires and functions as expected, but required true will not accept an empty value even when validated as true (as sort of expected), so the empty option remains required.

If required is false or not stipulated, no validation fires when clicking the submit button immediately after loading the form, although it does fire and function correctly when clicking in each individual field. In addition, successive submissions turn validation on and off like switch. In short, like a bit of a traffic jam.

Hope that is enough to go on, it's a bit of a brain hemorrhage I know.

Comment 48 by Raymond Camden posted on 4/7/2014 at 4:26 AM

"but required true will not accept an empty value even when validated as true"
I'm sorry, but I have no idea what this means. You said, if required is true, it won't accept an empty value... which is obvious, right? I'm totally not getting it.

Can you rephrase this whole thing? :) (Or maybe someone else in the thread groks it.)

Comment 49 by Chris Bowyer posted on 4/7/2014 at 4:55 PM

I was pretty tired the other morning, so it was difficult to see the forest for the trees. In short, however, the standard seems that remote validation must be required and the string must have length. Mind you, I am led to believe there is a way around this, but not being a jQuery guru doesn't help. What I was trying to do at the time, was run a regular expression, however, I have since found you can add methods with additional-methods.js included in the download, or write your own e.g.

jQuery.validator.addMethod("telephoneAU", function(value, element) {
return this.optional(element) || /^[0][2378]\d{8}$/.test(value);
}, "Please specify a valid Australian telephone number.");

Anyway, my problem is now solved.

Thanks for all the cool tutorials.