Enabling Client Side Validation on Custom Data Annotations With IClientValidatable in MVC

In this article you will learn how to create or enable client-side validation for custom data annotations. If you go through my previous article then you will learn How to Create Custom Data Annotations to enable server-side validation. I also recorded a video for this and uploaded on YouTube. This article is a continuation of that article so go and read the article or watch the video.

So, in the last article I explained creation of custom data annotations and created a demo custom annotation by name [ExcludeChar("/.,!@#$%")], this annotation excludes all the characters typed as a parameter from the Name field/property and displays the friendly message "Name contains invalid character.".

Well, let's look at this GIF to understand how it worked.

server-side.gif

You can see that when the user clicks on the button it displayed the message "Name contains invalid character.", so this is doing an unnecessary server round trip just to validate the Name. We can tweak and stop this server round-trip, just by a few quick codes.

Again, look at the GIF, this is what I wanted to create.

client_side_unob.gif
 

You can see that it works and displays the message "Name contains invalid character." immediately on typing a character. So nice.

I hope you have a working server-side validation sample here, if not then read the article or watch the video (links given above).

Let's go step-by-step from here.

Step 1: Adding IClientValidatable interface and its GetClientValidationRules method

This is exactly the same code I used in the last article, I made a few additions here that are highlighted.

  1. class ExcludeCharAttribute : ValidationAttribute, IClientValidatable  
  2. {  
  3.     private readonly string _chars;  
  4.     public ExcludeCharAttribute(string chars)  
  5.         : base("{0} contains invalid character.")  
  6.     {  
  7.         _chars = chars;  
  8.     }  
  9.     protected override ValidationResult IsValid(object value, ValidationContext validationContext)  
  10.     {  
  11.         if (value != null)  
  12.         {  
  13.             var valueAsString = value.ToString();  
  14.             for (int i = 0; i < _chars.Length; i++)  
  15.             {  
  16.                 if (valueAsString.Contains(_chars[i]))  
  17.                 {  
  18.                     var errorMessage = FormatErrorMessage(validationContext.DisplayName);  
  19.                     return new ValidationResult(errorMessage);  
  20.                 }  
  21.             }  
  22.         }  
  23.         return ValidationResult.Success;  
  24.     }  
  25.     //new method  
  26.     public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)  
  27.     {  
  28.         var rule = new ModelClientValidationRule();  
  29.         rule.ErrorMessage = FormatErrorMessage(metadata.GetDisplayName());  
  30.         rule.ValidationParameters.Add("chars", _chars);  
  31.         rule.ValidationType = "exclude";  
  32.         yield return rule;  
  33.     }  
  34. }  

 

The code above first derives the ExcludeCharAttribute class from the base class ValidationAttribute (in short, that allows server-side validation) and IClientValidatable (to enable client-side validation).

Second, I added a new method, GetClientValidationRule. Actually, the IClientValidatable interface defines a single method GetClientValidationRules. When the MVC framework finds a validation object with this interface present, it invokes GetClientValidationRules to retrieve a sequence of ModelClientValidationRule objects. These objects carry the metadata, or the rules, the framework sends to the client.

In order to run the validation on the client-side we need a few rules to return, like what error message to display when validation fails, which character(s) to exclude/not accept with Name and an identifier for a piece of JavaScript code that can exclude the words. So, this is what the GetClientValidationRules method is returning.

Step 2: Verity the appearance of data-val attributes in markup

If you run the application here then you will see the following data-val attributes added to the markup.

1.png
 

But this is not enough to start client-side validations in action because now we have metadata on the client, but we still need to write some quick script code to dig out metadata values from data-val attributes on the client and execute the validation logic.

Step 3: Add JavaScript code in a new .js file to execute on client-side

Just go and add a new JavaScript file to the "Scripts" folder by the name "excludechar.js" and use the following code:

  1. /// <reference path="jquery.validate.js" />  
  2. /// <reference path="jquery.validate.unobtrusive.js" />  
  3. $.validator.unobtrusive.adapters.addSingleVal("exclude""chars");  
  4. $.validator.addMethod("exclude"function (value, element, exclude) {  
  5.     if (value) {  
  6.         for (var i = 0; i < exclude.length; i++) {  
  7.             if (jQuery.inArray(exclude[i], value) != -1) {  
  8.                 return false;  
  9.             }  
  10.         }  
  11.     }  
  12.     return true;  
  13. });  

The first two lines of code will do nothing but allow the benefits of IntelliSense while writing JavaScript. Alternatively, we could add these references to the "_references.js" file.

The code that is highlighted is the masterpiece code, adapter. The MVC unobtrusive validation stores all adapters in the jQuery.validator.unobtrusive.adapters object, this exposes an API for us to use in the application.

I am using the addSingleVal adapter method that retrieves a single parameter value from metadata. The first parameter is the name of the adapter and it must match the ValidationProperty value we set on the server-side rule. The second parameter is the name of the single parameter to retrieve from metadata.

Like the adapters object, the validator object has an API to add new validators. The name of the method is addMethod that takes two parameters. The first is the name of the validator and by convention it matches the name of the adapter. The second is a function to be invoked when validation occurs. The rest of the code in the addMethod does nothing but loop through all the excludable characters (/.,!@#$%) and checks its existence in "value" that is nothing but the Name entered in the TextBox.

Step 4: Adding reference on Create.cshtml and Edit.cshtml Views

If you have done all the preceding then just go and add the reference of the JavaScript file for the Views, as in the following:

  1. @section Scripts {  
  2.     @Scripts.Render("~/bundles/jqueryval")  
  3.     @Scripts.Render("~/Scripts/excludechar.js")  
  4. }  
Keep it in mind that the excludechar.js file that we created should appear at the bottom and after the "jquery.validate.min.js" and "jquery.validate.unobtrusive.min.js" files.
 
Now run the application and enjoy client-side validation in action.
 
Hope this helps.