Unobtrusive Client Validation in ASP.NET MVC using AngularJS

June 28, 2016 by C#   JavaScript   ASP.NET   Angular   MVC  

When Microsoft released ASP.NET MVC 3 around October 2010, they introduced a feature called "unobtrusive client validation" into their codebase.

Which basically renders data annotations as defined on a Model / ViewModel property as HTML attributes on its bound element (TextBoxFor, DropDownListFor etc), in turn the jQuery validation plugin along with various jQuery adapters consumes the attributes on the client side (jquery.validate, jquery.validate.unobtrusive).

E.g. this

public class PersonViewModel
{
    [EmailAddress]
    [Required]
    public string Email { get; set; }
via this
@Html.TextBoxFor(m => m.Email)
outputs this

<input data-val="true" data-val-email="The Email field is not a valid e-mail address." data-val-required="The Email field is required." id="Email" name="Email" type="email" value="">
when configured like this

<configuration>
  <appSettings>
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />

But what if we need / want to use an alternative JavaScript library instead of jQuery? 

For example in recent years, Google's AngularJS stepped up onto the scene and quickly gained a lot of popularity among developers.

Initially it was claimed that Angular wasn't designed as a replacement for jQuery and that we can use both in our projects, but as things evolved a new best practice emerged "avoid using jQuery and Angular together", you can read more about it over here.

Obviously when using Angular in conjunction with ASP.NET MVC, this poses a little bit of a challenge, seeing that a lot of the Microsoft solutions are dependent on jQuery these days, e.g. SignalR, unobtrusive validation etc.

In this post we're going to focus on making unobtrusive validation work without jQuery using Angular - I've attached one of my demo projects to this post to illustrate its usage.


Now Angular features its own form validation attributes, e.g ng-pattern, ng-minlength, ng-maxlength, ng-required etc (similar to the existing generated attributes).

My first instinct was to investigate if Microsoft included a way to override the existing data annotations generated attributes (like a custom WebControlAdapter in WebForms) but couldn't find anything like that.


So I ended up mapping the data annotation rendered attributes to their Angular counterparts using a simple Angular directive like seen below.

(function () {

    angular
        .module('cstruter.validate.unobtrusive', [])
            .directive('val', validation);

    function validation($compile) {
        return {
            restrict: 'A',
            require: 'ngModel',
            link: function (scope, element, attrs, controller) {

                // Make DOM changes
                setAttributes(element, attrs);

                // Prevent directive from being fired again for the same element
                element.removeAttr('data-val');

                // Apply DOM changes
                $compile(element)(scope.$parent);
            }
        };
    }

    function setAttributes(element, attrs) {
        var attributes = {},
            set = function (name, key, value) {
                attrs[name] && (attributes[key] = value || attrs[name]);
            };

        // Attribute mappings, ASP.NET MVC to ng-attributes
        set('valRegex', 'ng-pattern', '/^' + attrs.valRegexPattern + '$/');
        set('valMinlengthMin', 'ng-minlength');
        set('valMaxlengthMax', 'ng-maxlength');
        set('valRequired', 'ng-required', true);
        set('valRange', 'ng-minlength', attrs.valRangeMin);
        set('valRange', 'ng-maxlength', attrs.valRangeMax);

        // Assign Attributes 
        element.attr(attributes);
    }

})();


The directive seen above (when included in the appropriate module and registered in a ScriptBundle) will match on data-val attributes and attempt to map them to their Angular versions.

Next we need to display the error messages somewhere, originally when writing this directive I simply added ngMessages to the DOM via the same directive, but felt that it is a very inflexible approach, so similarly to the attributes I looked for a way to provide custom markup for the ValidationMessageFor extensions. 

But ended up being forced to write separate helpers - ValidationAngularMessageFor and BeginAngularForm.

The Angular Form helper can be seen below.

public static MsAspMvc.MvcForm BeginAngularForm(this HtmlHelper htmlHelper, 
string actionName, string controllerName, FormMethod method, object htmlAttributes) { RouteValueDictionary routeValues = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes); if (!routeValues.ContainsKey("name")) routeValues.Add("name", "form"); htmlHelper.ViewData.Add("formName", routeValues["name"]); return MsAspMvc.FormExtensions.BeginForm(htmlHelper, actionName, controllerName, method, routeValues); }


The reason for writing this helper is simple, I needed the name of the parent form as required by Angular Validators but couldn't retrieve it via the standard helper (via a directive the FormController could have been used using ^Form require), is there a clean way to use the Angular validation without needing the name of the form, anyone?

But perhaps this helper could be used for other Angular specific functionality as well in the future?

You can have a look at the ValidationAngularMessageFor helper over here (not going to post it on here for abbreviation sake), it simply renders the data annotations for a property as ngMessages, like seen below.

<div ng-messages="form.Email.$error" ng-show="form.$submitted || form.Email.$dirty" role="alert">
	<div class="error" ng-message="required">The Email field is required.</div>
	<div class="error" ng-message="email">The Email field is not a valid e-mail address.</div>
</div>

Well, that is all I want to say about the subject for now, be sure to look at the github project, I am planning to use this project for all my ASP.NET MVC demos in the future, so you will notice a few interesting stuff on there.

E.g. The controllers use Ninject,  the inclusion of the uber useful NugetContentRestore MSBuild task etc.

Additional Reading

Unobtrusive Client Validation in ASP.NET MVC 3
AngularJS Form Validation


Leave a Comment




Related Downloads

ASP.NET MVC Demo application