Unobtrusive custom validator for MVC3

Prerequisites

  • MVC 3
  • JQuery / JQuery UI
  • Microsoft Visual Studio 2010

Introduction

In this blogpost I wanted to demonstrate how ‘easy’ you can build your own custom validation attributes with client side validation in JQuery. This client side validation is implemented just like the new unobtrusive client validation from MVC3. First I show you how validation works in MVC3, then I implement a new custom validator. After implementing the custom validator I’ll wrap the demo up with some JQuery sauce…

Setting the stage

Let’s build a blog site, and I know, it has been done before :). Start by creating a new MVC3 Web Application project, name it anyway you want, choose empty as option and click ‘OK’. I named mine “MySuperBlog”. After the project is created, add a new database by adding a new item and selecting “Data->Sql Server Database”. Let the database be generated in the App_Data folder. Create a simple table in the database, name it Blog and give it the following columns:

Name Type Size Key
Id int   PK (identity)
Title nvarchar 50  
Body nvarchar 1024  
Author nvarchar 50

Create a new Entity Framework ORM by adding a new item in the model folder from the item type “ADO.NET Entity Data Model” in the category “data”. Let the model be generated from a database, choose the Blog database and check if the Blog table is selected.

After the wizards completes, you should see something like the following screenshot:

Create a new BlogController by rightclicking the controllers folder and selecting the appropriate command from the context menu.
Be sure to check the checkbox to let Visual Studio generate the CRUD operations for us. Change the Create action method to accept a Blog class as an incoming parameter and Build the project. After the build completes, create a new view for the create action method by rightclicking in the action method and choosing ‘Add view…’ from the context menu, like this:

Create a strong typed view, select ‘Create’ as scaffold template and leave the rest at their defaults. Remember to build the project before the Blog class is available in the drop down. Test the site by navigating to /Blog/Create to check if the form renders as expected.

We are now done building the stage for the rest of the demonstration.

Implementing Validation

Implementing validation in MVC3 is easy. You just have to add a few attributes to the properties of your model class and the rest is taken care of. However, if we would apply these attributes in the generated code by the Entity Designer, those would dissapear after the classes were regenerated. Therefore we apply the buddy class technique, described here.

Create a buddy class for the generated Blog class and apply some required field attributes on the properties Title and Body, like I did below:

Change the code in the BlogController Create action so it checks for a valid state after a form post. If the state is invalid, it should return the create view, otherwise it should redirect to the index page. The ModelState.IsValid property lets us know if there were validation errors in the submitted form. The changed code should resemble the code below:

Test the site, when you leave the field empty, the browser should display validation errors.

Be sure to check the source code for the page.

Note that there is no inline javascript for the validation. Instead of inline javascript, the entry fields are marked with the data- attributes. These HTML5 attributes let us append extra data to HTML tags which we can use later in our logic, validation for example. This logic is implemented in the validation jquery scripts that are linked on top of the page.

Create our own custom validator

What I wanted to show is a new custom attribute for validation, so it can be applied to any property in the model, which submits the same unobtrusive data attributes as the attributes I’ve shown before. The next step would be to include/inject a new validation pluging which does the client side validation, the same as the existing validation rules. Lets break this task down into two steps.

  • Create a custom validation attribute with appropriate validation logic
  • Extend the custom attribute wiith clientside logic in an unobtrusive manner

Lets dive into this.

Create a custom validation attribute with appropriate validation logic

To implement a new custom validator attribute is relatively easy. Add the new class to the Models folder and name it DomainNameValidator, we want a developer to assign this attribute to a field in his model that requires a domainprefixed entry like ‘Domain\John’. The effect of this attributes is that only entries filled with a given domainprefix are allowed to be processed.

I know that there are other ways to test this kind of validation, but I merely want to address the technique.

Let the new class derive from ValidationAttribute and override the IsValid method. Implement a public field to hold the domainname to check for and implement the logic in the IsValid override. For inspiration purposes, here is my code:

Now we can test our newly created attribute by applying it to a field from the blog model. Apply the DomainName validator to the author field of the BlogMetadata class, like so:

Test the site. If all went well, we should get a validator error AFTER submitting the form with some test data. The screenshot below illustrates this:

Ok, that is nice, but I wanted to implement clientside validation. Lets go a step further in that direction.

Extend the custom attribute wiith clientside logic in an unobtrusive manner

As stated before, we want to implement clientside validation, just like the rest of the validation logic in our page. We need to generate code which injects data attributes for validationrules and we need to write javascript code to do the actual validation. Let me take a step back and show the markup that was generated by the required field validators:

Notice how ‘data-val-required’ is applied as an attribute for the input fields? This is the validationtype attribute the jQuery validators use to do their logic. ‘Data-val’ is fairly generic and means something like data-validation attribute, the last ‘required’ keyword specifies the rule to apply when validating this input field. We have to generate these attributes for the fields that are marked with our DomainNameAttribute, so lets implement this logic.

I want to generate markup that ends up looking like ‘data-val-domain’. It is not a rule the jQuery validators understand just yet, but later in this post we will implement this rule there aswell.

Change the code for the DomainNameValidatorAttribute so it implements the IClientValidatable interface and implement the single method that comes with the interface, ‘GetClientValidationRules’. This method returns the data needed for the framework to ouput the data attributes in the resulting HTML.

We have to return an IEnumerable of ModelClientValidationRules. A ModelClientValidationRule describes the rule name and optionally a collection of parameters that go with the rule. In our case it would be handy to provide the DomainName property as a parameter, so we have this information on the client to check against.

The code for this method is as follows:

Now when you run the site again and inspect the HTML from the create blog page, you should see added attributes for the author field with regard to data validation, like you see in the screenshot below:

See how the serverside code resulted in data-val attributes and how the extra parameter got rendered. This looks promising :)

Complementing the clientside validation with jQuery

All that is left to do is adding a jQuery plugin that implements the domain check rule. For those who have never written a jQuery plugin before, I would like to recommend this link.

Add a new JavaScript file to the Scripts folder and name it ‘domaincheck.js’. This file contains the plugin that implements the validation logic. Add the following script to this file:

// add the method as a validator plugin....
(function ($) {
    $.validator.addMethod("domaincheck", function (val, el, params) {
        if (this.optional(el)) return true;
        return val.split('\\')[0] === params;
    });

    // register the validator
    $.validator.unobtrusive.adapters.add("domain", ["name"], function (options) {
        options.rules["domaincheck"] = options.params.name;
        if (options.message) options.messages["domaincheck"] = options.message;
    });
} (jQuery));

This piece of JavasScript is boilerplate for every rule you want to add.

The first section adds a new method to jQuery with the name ‘domaincheck’. This method does the actual validation logic we implemented before in the serverside equivalent.

The second section registers a new adapter/validationtype. Now when the data-val-domain is read, the domain adapter handles the validation by calling the checkdomain method.

Note how the second parameter “[‘name’]” for the Add method specifies which extra parameter data attributes have to be passed to the domaincheck method when validating the input field. This extra parameter is the third parameter in the checkdomain method. We have to test if the value provided contains a domain name that matches the domainname attribute in the markup.

After savind this JavaScript file, all that is left to do is to include this script file somewhere in the page.

Modify the Razor masterpage (Views/Shared/_layout.cshtml) to include a section names ‘Scripts’ into the header of every page. This way we can define per-page scripts. The result should resemble the following:

After this, we can modify the page create.cshtml to include the script for our custom clientside domainname validator. Change the create_cshtml so it contains a script section that includes the script domaincheck.js we wrote earlier. It should look like this:

Note the extra @section scripts block, registering the script for this page.

Try and run the site to check if we now have successfully implemented clientside validation. Mine worked just fine, allowing only DOMAIN\somevalue entries (or empty ones for now)….

Adding some jQuery sauce

Now we’ve created a basic edit entry, but it does not look much like a modern webpage. It is missing fancy UI and some AJAX functionality to insert new blogs. Lets spice up this demo with some fancy features.

We want to insert blog entries, the AJAX way, this means we want to post data to the controller, expecting just data back and not a whole page. Lets modify the controller so, when a valid blogpost is submitted, the returnvalue is the title of the blog post. In all other cases, return an error message..

The code for the create action for the BlogController should look as follows:

Note how we return the title of the blogpost as the returnvalue for this action.

Also Note how we expect a Blog parameter as an incoming parameter. MVC3 has JSON Model Binders that are capable of translating JSON to object instances..

Add the jquery UI javascript and CSS files to your project. These can be downloaded from the jQuery UI site here. A screenshot from the project with these files is shown below:

These files are referenced from the Razor masterpage, _layout.cshtml. Make sure the names match the files you’ve included in the _layouts.cshtml page.

Lets finally modify our user interface (view) to show a dialog for the entry of a new blogpost and submitting the data to the server in an AJAX fashion. A screenshot of my implementation is shown below.

Tip -> You can click on the image to display a readable version.

I won’t go into the details here, maybe in a future post. The most important changes are that I wrapped the form in a div which I gave an id of “dialog”. In the document_ready code I wrote javascript that makes the div a dialog. I’ve added a hyperlink which opens the dialog, as you can see.

Lastly I’ve wrote a submit handler which uses the jQuery ajax method to post the data in a JSON format to the Blog/Create action. On a successful create, I add the newly added post to the bloglist on the page.

The result is a blog create page that looks like this:

neat eh?

Summary

In this, rather large demo, I showed you how to create a custom unobtrusive validator attribute for use on your models. I’ve also given an example, but not an excessive explanation, of how to jQueryUIify and AJAXify your application.

Interesting stuff.

About these ads

12 comments so far

  1. zaaaaphod on

    Thanks very much – that was really helpful :-)

  2. Paul on

    This has been very helpful! Do you happen to still have the solution?

    thanks

    • johnmj on

      Yes, I has been a while, I probably have it at the office. I’ll check it a.s.a.p.

  3. Anand on

    Hi John,

    Thanks a lot for this post, i have implemented following the same, and all built in client validators work fine except for my custom validator. The scripts are firing, but the error message do not show up on client side page. Any thoughts please. I do not know what wrong i have done, all seems to fall in place.

    i have my code snippet and details on this stack overflow post for further details ( http://stackoverflow.com/questions/8634986/custom-model-validation-using-data-annotations-mvc3-error-messages-not-shown-o ) .

    Appreciate your kind help, Thank you

    • johnmj on

      You can email me for help anytime.

  4. Gabriel Gois de Melo on

    Excellent post, very helpfull

    Thanks!

  5. Raj on

    Hi,

    I am new in mvc3.
    I read this post and i understand that this is both side of validation client and server but if i want to use only client side validation then is it necessary to use Annotation in model like ([required])? or is it require to change some thing in model with jquery.validate.unobtrusive type for only client validation.

    • johnmj on

      You could just implement the iclientvalidatable and have client side validation. I doubt if this would be wise, since someone could disable javascript, causing a bad form to be posted to the server.

  6. Pughal on

    Microsoft JScript runtime error: ‘Sys’ is undefined— i got this error in my js file ,while executing . pleasse help me to solve this

    • johnmj on

      You probably do not have all the necessary includes linked on the page?
      I suspect that the Microsoft.Ajax library is not included or loaded in the page..

      John.

  7. skparmar on

    Very Nice Post!


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

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: