Background: JQuery Validator is JQuery plug-in for validating html form fields. Microsoft has included the JQuery Validator as part of Microsoft?s new CDN, and it looks like JQuery and JQuery Validator will both be included with Asp.Net MVC 2. You can find Part 1 here.
From where we left off, the next thing to do is validate a field via a web service.
AJAX Validation
There are actually two ways of validating fields via an ajax call. The first one I?ll show it the built in way. The second is more of a hack, but gives a good background on what is actually going on with the JQuery Validator.
So in part 1 we showed how to do simple validations of a form. We will start with a simpler form for this example:
1: <form id='UserNameForm'>
2: <input id='userNameEdit' type='text' />
3: <button type='submit'>Submit</button>
4: </form>
Obviously the form can be more complex, but I like starting simple.
The JQuery Validation script should look something like this:
1: $('#UserNameForm').validate({
2: rules: {
3: userNameEdit: { required: true }
4: }
5: });
That script will make sure there is something in userNameEdit. But now we want to make sure the user name is unique in the system. So to start, I need a web service to call. For this example, I?m using Asp.Net MVC.
1: public JsonResult VerifyUserName(string userNameEdit)
2: {
3: return Json(true);
4: }
OK, a few things about the web service above. 1. in the current implementation, all user names are valid. That is the Json(true). If you want to see the failure case, change it to Json(false). 2. look at the method parameter. It is the same name as the form field. That is a convention in the JQuery Validator, and near as I can tell you cannot override that behavior. Third: JsonResult stipulates that the method will return its data in the Json format. Unlike other web method types, where the output type come from the caller, this one only returns the specified format.
Validation Method 1: remote
Now, lets modify our script from above to call that web method:
1: $('#UserNameForm').validate({
2: rules: {
3: userNameEdit: { required: true, remote: "<%=Url.Action("VerifyUserName", "Account") %>" }
4: }
5: });
The new part is the remote in the script. You give it the name/location of your web service to call to validate the field, and the field name/value are passed in.
Now about the Url.Action bit in there?If you are not used to Asp.Net MVC, that line will look strange to you. Here is how I understand it: Url.Action is a method that utilizes the Asp.Net Routing engine to create urls. In the case of MVC, I need a url to a controller method. The controller is ?Account? (the actual class name is AccountController), and the method is ?VerifyUserName?. The cool part, now you can get a valid url no matter where you are in the project directory hierarchy (that is a problem with WebForms).
But there are downsides: I hope the only data you are going to pass to the method is the data located in the control. If you need to pass more information to do the validation you could be in trouble.
Validation Method 2: Validator.addRule
The next option is to add a custom rule that make the ajax call and does the validation. So first we add the custom rule:
1: function VerifyUserName(value, element) {
2: if ( this.optional(element) )
3: return "dependency-mismatch";
4:
5: var previous = this.previousValue(element);
6:
7: if (!this.settings.messages[element.name] )
8: this.settings.messages[element.name] = {};
9: this.settings.messages[element.name].verifyUserName = typeof previous.message == "function" ? previous.message(value) : previous.message;
10:
11: if ( previous.old !== value ) {
12: previous.old = value;
13: var validator = this;
14: this.startRequest(element);
15: var data = {};
16: data[userNameEdit] = value;
17: $.ajax($.extend(true, {
18: url: '<%=Url.Action("VerifyEmail", "Account") %>',
19: mode: "abort",
20: port: "validate" + element.name,
21: dataType: "json",
22: data: data,
23: success: function(response) {
24: var valid = response === true;
25: if ( valid ) {
26: var submitted = validator.formSubmitted;
27: validator.prepareElement(element);
28: validator.formSubmitted = submitted;
29: validator.successList.push(element);
30: validator.showErrors();
31: } else {
32: var errors = {};
33: errors[element.name] = previous.message = response || validator.defaultMessage( element, "verifyUserName" );
34: validator.showErrors(errors);
35: }
36: previous.valid = valid;
37: validator.stopRequest(element, valid);
38: }
39: }, '<%=Url.Action("VerifyEmail", "Account") %>'));
40: return "pending";
41: } else if( this.pending[element.name] ) {
42: return "pending";
43: }
44: return previous.valid;
45: }
The only thing interesting in that ajax call (as far as JQuery is concerned) is the async = false bit. That line is telling JQuery to execute this method serially so I can return the result (hope the method doesn?t take too long). But I can customize the AJAX call however I need.
Next, we can edit our ?validate? method to use the custom rule.
1: $('#UserNameForm').validate({
2: rules: {
3: userNameEdit: { required: true, verifyUserName: true" }
4: }
5: });
So what I did here was take the code from the Validator?s Remote method, and modify the parts I needed for my own purposes. That said, I had a lot of trouble getting it to work correctly. I?m sort of hoping someone will tell me what I did wrong actually.
Validation Method 3: Overloaded Submit
The final way I?m going to show to use an AJAX web method to perform a validation check for use is by overloading the submit callback. This what I?ve actually ended up using for a number of projects. It is the simplest to code and understand.
1: function CustomSubmit(){
2: var _val = $("#CustomerForm").validate({
3: rules: {
4: FirstName: { required: true },
5: LastName: { required: true },
6: Email: { required: true, email: true}
7: },
8: messages: {
9: FirstName: "First name is required",
10: LastName: "Last name is required",
11: Email: {
12: required: "Please enter a valid email address",
13: minlength: "Please enter a valid email address",
14: remote: jQuery.format("{0} is already in use")
15: }
16: },
17: submitHandler: function(form) {
18: var data = {userNameEdit:$("#Email").val()};
19: $.ajax({
20: url: '<%=Url.Action("VerifyEmail", "Account") %>',
21: dataType: "json",
22: data: data,
23: success: function(response) {
24: var valid = response === true;
25: if ( valid ) {
26: form.submit();
27: } else {
28: _val.showErrors({ "Email": 'Email is already in use.' });
29: }
30: }
31: });
32: }
33: }
34: }
A couple of key points on this one as well. First notice what is happening on line 2. I?m saving the resulting validator so I can use it later. Then action happens at line 17 with the submitHandler. submitHandler is a callback (event) which gives you a hook into the normal submit process. Also you can see on line 26 I am calling the form.submit myself. Then on line 28 I am displaying a custom error message.
Summary
Finally, no matter how much checking you do in the client, you still have to recheck everything once you are on the server ? sorry it is true. Until I find a better way to implement Method 2, I will be using Method 3. It is the most straight forward way to do validation via AJAX methods I have found so far.
If I can get to a Part 3 (Part 2 took WAY too long), I?ll look into using AJAX to handle the submit, and displaying custom error messages when the submit goes wrong.
Small tip on making javascript maintainable: Put it in a separate file and don’t inline server code 🙂 Of course it works for a demo 🙂
Hello Chris,
When you get a chance, check out MVC2 Preview 2. This framework solves the client/server-side validation dilema.
This is easily implemented in three steps. First, add attributes or DataAnnotations, to your domain model. Then add these three scripts already included in your MVC project: jquery-1.3.2.js, jquery.validate.js, and MicrosoftMvcJqueryValidation.js. Finally add this line of code to your View:
There are several advantages here. Client-side validation is the default and if the user has javascript disabled server-side validation is conducted. All of your validation rules are in one location so it is easy to maintain, and you have accounted for both client and server-side validation. What I do not like is it takes the fun out of writing your own jQuery 🙁
For an easy example read this:
http://blogs.msdn.com/rickandy/archive/2009/10/03/client-side-validation-for-mvc-2-p2.aspx
Thanks for your post,
James
If you want to pass more data to the “remote” rule, you can customize the call a lot by passing an options object instead of just the url, like:
remote: {
url: ”,
type: ‘post’,
data: {
user: $(“#userNameEdit”).val(),
otherParam: 123
}
}
If you want to include data that will be evaluated at the time of the validation and not only once when the page is loaded, it seems that you can even include fields in the form of functions :
remote : {
url: ‘ManageClients.do’,
data: {
action: ‘validateUniqueField’,
id: function(){return $(‘#clientFormDialogId’).val();}
}
}
Like that, the $(‘#clientFormDialogId’).val() will be run each time the validation is executed.
In my case it was necessary because the form is inside a Dialog widget and it is populated via AJAX, so the same form can be used for several objects, and the id element will have no value at all when the page is first loaded, and its value dynamically changes every time an object id loaded into the form.