Adrian Grigore

ASP.NET MVC Tip #4: Client-side form validation made easy – Part 2

In ASP.NET MVC on January 11, 2010 at 15:00

In my previous article about ASP.NET MVC Client-Side validation, I showed how to set up your project so that you don’t have to write any custom JavaScript code for any new validation rules. This approach also covered remote client-side validation – these are rules which require server-side resource in order if a particular input field is valid or not.

Since I wrote the first part of the article, Steve Sanderson adopted my idea of automatic client-side validation and baked a similar feature right into the latest version of xVal.

One important thing is still missing though: What do you do when validation depends on server-side resources but also involves several different form fields? For example, let’s assume you were implementing a form for your website users to update their contact information.

Figure 1 – Example update form
Figure1 - Example update form

The form is very similar to the signup form from the last part of this article. However this time instead of adding new data to the database, the user needs to update his e-mail address. Validation still has to check that the e-mail address entered does not already exist yet. However, this time it should ignore the current user’s e-mail address.

For example, if the current user’s e-mail address is adrian@lobstersoft.com, then we still want to accept this as valid input. But if he enters “john@doe.com” and that e-mail address is already taken, we want client-side validation to fail.

Figure 2 -  Client-side validation in action

And all of this should of course work automatically on both the server and client side simply by providing a server-side validation rule and attaching it the Viewmodel:

Listing 1 – ViewModel with client/server side enabled entity validation attribute

    [EmailUnique]
    public class UserModel
    {
        public int ID{ get; set;}

        [Required(ErrorMessage = "E-mail address is missing.")]
        [RegularExpression(EmailRegEx, ErrorMessage = "Invalid e-mail address.")]
        [EmailUnique]
        public string Email { get; set; }

        public class EmailUniqueAttribute : RemoteEntityValidator
        {
            public EmailUniqueAttribute()
                : base(new string[] { "Email", "ID" }, typeof(User))
            {
                ErrorMessage = "Someone else has already signed up with this e-mail address.";
            }

            protected override bool EntityValid(object value)
            {
                User entity = (User)value;
	      //make sure there is no user with a different ID but same e-mail address
                return !new FakeUsersRepository().LoadAll().Any(u => u.ID != entity.ID && u.Email == entity.Email);
            }
        }

	// Other UserModel properties and validation attributes...
}

Behind the scenes

Validation of the e-mail input on this form depends on two variables: The user’s e-mail address and his user ID.

Therefore we need to:

1. Store the user id as a hidden form input field
2. Provide a client-side Javascript function that creates a remote validation rule that submits all relevant form input fields for the particular validation rule to the server.
3. Provide a server-side action method that triggers the validation attribute’s IsValid method and returns the result to the client-side remote validator.

The approach is quite similar to remote property validation, except that multiple form input fields have to be provided to the server, but there are also a few other other minor differences. For example the client-side remote validation rules must also be re-validate when one of the other form input elements that validation depends on is changed.

But the good news is that all of this can be still solved in a generic way so that you can get away just by coding your server-side data annotation validation attributes – Very similar to the technique shown in the first part of this article.

Using the RemoteEntityValidator

For using the code provided with this article in your own project, proceed as follows:

1. Add Xval, jquery.validate, DataAnnotationModelBinder and the code provided in this article’s demo project to your project as shown in the first part of this article.
2. Next, whenever you need to write a validation attribute that depends on multiple properties of your view model, derive your validation attribute from RemoteEntityValidator as shown in Listing 1.

These are the differences you have to mind in comparison to the RemotePropertyValidator from the first part of the article:

• The RemoteEntityValidator’s constructor takes one more variable than the RemotePropertyValidator: A string array of all property names in the ViewModel which are relevant for running the validation rule. I guess this could also be done in a more type-safe way by using Linq expressions, but I chose strings for the sake of a simpler syntax, especially when handling many different form fields.

• Note that even though the RemoteEntityValidator depends on several model properties, you must not apply it to all relevant properties in your model. Just apply it to the ViewModel entity and the property that corresponds to the html input element where you want the client-side validation error to appear. However, you do have to apply it both to the ViewModel property AND to the ViewModel class. The property-level attribute is needed for the triggering client side validation when the input element is changed, and the entity-level attribute is needed for server side validation to work.

• Your html form needs to have an ID attribute. It could be done without, but in my project this led to some problems with client-side validation when the form was reloaded via ajax. That’s why I changed the client-side code for retrieving form input values to something more stable since posting the first part of this article.

Download the demo project

Conclusion

You have learned how to implement client- and server-side validation in a completely generic, model-oriented way. This part of the article completed the technique shown before with validation attributes that depend on several model properties.

Advertisements
  1. Thanks, Adrian, for your work on this and for the examples. This sounds exactly like what we’re looking for. However, I’m having a problem with the version of System.ComponentModel.DataAnnotations that you’re using.

    The version in your sample project is 99.0.0.0, but in mine, it’s 1.0.0.0. Whenever I try to add this to my project (without changing anything else), my client-side validation no longer works. I’m referring to the basic validation that’s built into xVal, I’m not even to the point of trying the custom server side stuff yet.

    I would use the older version of DataAnnotations, but your code uses some of the newer features.

    Here’s some other info:

    jQuery – 1.4
    jQuery Validate – 1.6
    xVal – 1.0 (I notice your project uses 0.8, but reverting to that did not solve my issues)
    MVC – 1.0

    Any ideas?

    Thanks in advance.

  2. @Jerad: The reason that my version of the DataAnnotations is 99.0.0.0 is that I downloaded it and compiled it from source when it was first released. I had no problems with it (other than the one described in part 1 of my artcle), so I stuck with it so far.

    I’m not sure why client-side validation no longer works when you exchange it. If server-side validation is still working it can’t be missing support for buddy classes.

    Have you checked the client-side code that’s generated by xVal? If it corresponds to your validation attributes, then the problem is on the client side. If not, then something’s wrong with the data annotations and my next step would be to compile xVal and the DataAnnotationsModelbinder from source and debug it to see what exactly is not working.

  3. I prefer doing client side validation with jquery and the validator plugin. It is so easy to use and has built in pattern matching for email, credit cards, urls and more. See my blog post for more details: http://www.codecapers.com/post/Client-Side-Validation-with-jQuery.aspx

  4. My approach also uses jquery.validate for the client side. The difference to your approach is that you are writing client-side validation rules separately in javascript whereas my approach generates these from your model definition for you.

    This is not only less effort, but also less error prone since means that even if you add some more validation rules or some new properties to your model, client-side validation always stays up to date automatically.

  5. Hi,

    I use asp.net mvc 2 beta 2, I cant compile the project. Maybe bindingContext.ValueProvider change in asp.net mvc 2 beta 2. If you have spare time, would you please update the code with the latest version. Thank you very much.

    newbie

  6. Hey ,
    I got a typical Issue.
    I m using Client side validations for ASP.NET MVC Application but while Hosting Application on IIS7.0 Client side validations were not working .However the same Client Validations are working on the Visual Studio Development Server 2010.

    Need a Solution.

  7. Greetings I recently finished reading through your blog and also I’m very impressed. I really do have a couple inquiries for you personally however. You think you’re thinking about doing a follow-up submitting about this? Will you be likely to keep bringing up-to-date as well?

  8. Hi Nella,

    Thanks for your kind comment. Yes, I do intend to post new ASP.NET MVC tips. I have yet to work with ASP.NET MVC for one week without learning something new that helped me improve my skills on the way.. It’s just that I’ve been very busy working on my current project lately, so I could not find any time for blogging.

  9. May I say that your information has aided me considerably, keep up the great do the job.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: