Applying unobtrusive jquery validation to dynamic content in ASP.Net MVC

The new unobtrusive validation features of ASP.net MVC are great, but I was surprised to find that once the validators have been applied for a document, validators as a result of dynamic content will not be applied, even if you call $.validator.unobstrusive.parse

The Microsoft jquery.validate.unobtrusive library calls the jquery .validate method on the relevant form. Inside this method, it does nothing if the form already has a validator attached, so any parsed rules will be ignored.

validate: function( options ) {
......	
// check if a validator for this form was already created
		
var validator = $.data(this[0], 'validator');
		
if ( validator ) {
			
  return validator;
		
}

......

To get around this, I've created a library extension for jquery.validate.unobtrusive.

/// <reference path="jquery-1.4.4.js" />
/// <reference path="jquery.validate.js" />
/// <reference path="jquery.validate.unobtrusive.js" />

(function ($) {
  $.validator.unobtrusive.parseDynamicContent = function (selector) {
    //use the normal unobstrusive.parse method
    $.validator.unobtrusive.parse(selector);

    //get the relevant form
    var form = $(selector).first().closest('form');
  
    //get the collections of unobstrusive validators, and jquery validators
    //and compare the two
    var unobtrusiveValidation = form.data('unobtrusiveValidation');
    var validator = form.validate();

    $.each(unobtrusiveValidation.options.rules, function (elname, elrules) {
      if (validator.settings.rules[elname] == undefined) {
        var args = {};
        $.extend(args, elrules);
        args.messages = unobtrusiveValidation.options.messages[elname];
        //edit:use quoted strings for the name selector
        $("[name='" + elname + "']").rules("add", args);
      } else {
        $.each(elrules, function (rulename, data) {
          if (validator.settings.rules[elname][rulename] == undefined) {
            var args = {};
            args[rulename] = data;
            args.messages = unobtrusiveValidation.options.messages[elname][rulename];
            //edit:use quoted strings for the name selector
            $("[name='" + elname + "']").rules("add", args);
          }
        });
      }
    });
  }
})($);

To use this extension, place it in a file loaded subsequent to jquery.validate.unobtrusive.js. Calling the method is simply:


var html = "<input data-val='true' "+
           "data-val-required='This field is required' " +
           "name='inputFieldName' id='inputFieldId' type='text'/>;
$("form").append(html);

$.validator.unobtrusive.parseDynamicContent('form input:last');

Conclusion

This post has provided a technique for enabling the consistent use of unobtrusive data-val-* style validators to dynamically created content.

kick it on DotNetKicks.com

About these ads
This entry was posted in ASP.Net MVC, jQuery and tagged , , . Bookmark the permalink.

46 Responses to Applying unobtrusive jquery validation to dynamic content in ASP.Net MVC

  1. Michael Reyeros says:

    Thank you very much for this series of articles. Can you tell me if there is anyway to have the framework automatically wire up the data-val* attributes on the controls or do we need to manually create and apply the attributes to the dynamic content? I have a view that initially calls a partial view passing in the main viewmodel. This partial view is bound to a complex property on my main viewmodel. The partial view simply contains a set of cascading dropdown lists. On initial load of the page these two dropdown lists’ validation works perfectly if I try to submit without selecting proper values. I also have another button on the page that if clicked loads another instance of the partial view on the page. If I now try to submit the form these controls, although they are bound to the same model and although I have set the correct .ValidationMessageFor helpers, no validation appears for them since the dropdownlists do not appear to be generated with the data-val* attributes. Is there any way that I can get them to appear correctly?

    • xhalent says:

      Hi Michael,
      I checked this behavior out and found that that although the data-val attributes were being rendered correctly by MVC to the browser, no parsing of them of was being performed. The jquery.validate.unobtrusivelibary only parses the document once after the intial document is ready. For subsequent Ajax requests, you need to explicitly call the parse. But it’s not too bad, simply at the end of the form in the partial view, at the following script:

      $(function () { $.validator.unobtrusive.parse(‘[select to the target element for your jax post-back’); });

      hope this helps!

  2. Erick says:

    Hi,
    I tried your javascript, but it didn’t work… then I looked at the validator, and found that you have to remove the “validator” data first so that the validator picks up the validations.

    So basically I added this code and put the form selection at the top.
    //get the relevant form
    var form = $(selector).first().closest(‘form’);
    //need to remove the validator data so that the validator picks up new validation.
    form.removeData(‘validator’);

    Cheers,
    Erick

    • xhalent says:

      Hi Erick,
      I’m not sure of your particular situation, but the script most definitely works if applied as I’ve instructed. Can you provide some more detail on your usage so I can extend the functionality to suit?

      The reason why I’m not keen on simply removing the validator from the form data is that you’ll be blowing away any manipulation of the validator that may have taken place between loading the form and wanting to apply your validations. For example, it is pretty common to turn validations on an element on or off depending on the state of another element on the screen. If you go destroying the validator state, then you need to handle that. The idea of my script was to not affect innocent bystander validations.

  3. hwiechers says:

    This really helped me out. Thanks!

  4. Cymen says:

    Am I right that this plugin will not work when one is attempting to change the validation rules of existing elements? That is what I’m interested in doing…

  5. Cesar says:

    Hi, I’m trying your example, but when I execute “$.validator.unobtrusive.parseDynamicContent(‘form input:last’);” I receive “undefined” on firebug console, and don’t work.

  6. Austin Floyd says:

    Awesome job on the work!! I was fighting this for an hour before I found your solution…

    I would recommend to change the lines that read:
    $(‘[name=' + elname + ']‘).rules(“add”, args);

    to be:
    $(‘[name="' + elname + '"]‘).rules(“add”, args);
    or
    $(String.format(“[name='{0}']“, elname)).rules(“add”, args);

    Reason being is that with JQuery 1.5 the “value” of the get by attribute name should be surrounded with quotes

  7. Sam Huggill says:

    Awesome, very helpful thanks, I hit this problem and was scratching my head!

  8. trouf says:

    Thank you for this! A very elegant solution to a problem that should not even exist, but still does.

  9. jimmy says:

    Awesome. Thank you. I’m putting together an internal web app for a non-profit and this helps me a great deal.

  10. Pingback: Isolated » MVC3 Ajax partial view validation

  11. Muhammad Adeel Zahid says:

    Hello,
    xhalent, i struggled to implement this solution on my asp.net mvc 3 application. i also add the content dynamically and it has all the data-val attributes but it does not work. i also tried the way you instructed but without any luck. when i probed it a little bit, it seems that line
    var unobtrusiveValidation = form.data(‘unobtrusiveValidation’);
    is creating problem. when i inspect unObtruseveValidation object after adding new fields (with all data-val attributes), it still contains only rules and messages for fields that were initially rendered on the screen. any ideas why?

  12. StevieG says:

    Thanks for this, saved me loads of time!

  13. Pingback: ASP.NET Classic & ASP.NET MVC 3.0 Validation

  14. Pingback: The Silence of The LaMs » Unobtrusive validation in partial Views

  15. Jason says:

    I tried implementing this for my project and it partially works. What I mean by partially is that the background color of my text boxes turns the nice shade of pink when there is an error but the data annotation messages for my properties are not displayed. It appears that this line in the jquery is not returning anything: unobtrusiveValidation.options.messages[elname]; Any ideas?

    • xhalent says:

      Have you put in the ValidationMessageFor span next to the input element?

      • Jason says:

        Yes. In fact, here is my phone number editor template:

        @model NestedBindingListTest.Models.Person.PhoneNumber

        Telephone Number
        @Html.TextBoxFor(x => x.Number)
        @Html.ValidationMessageFor(x => x.Number)

  16. Jeroen Rikhof says:

    Thanks, very nice article. Just two litte things what i was missing for my implementation to get working:

    @using (new MvcForm(ViewContext))
    {
    @Html.EditorFor(m => m.Properties)
    }

    And the quotes (elname was “Propery[0].value”, so it gets an error)
    $(‘[name="' + elname + '"]‘).rules(“add”, args);

  17. Jason says:

    Great post. You just saved me a ton of time trying to figure out why validation of my dynamic items wasn’t working. Appreciate it!

  18. Ragnar Ă–sterlund says:

    Thank you, very nice!

  19. Ryan O'Neill says:

    Excellent work, that javascript looks pretty complex. As others have said, it does break unless you put the quotes around the name selectors though.
    for example, I have id’s of the format ‘Diner[4]_Name’ generated by MVC enumerating a collection and jQuery breaks when looking at those without the quotes.

    Thanks for sharing.

  20. jonk says:

    Very nice, thanks for sharing this! However, I am encountering some problems with dynamic nested lists. I am using the solution from Joe Steven’s blog, allowing the user to dynamically add/edit up to 4 levels of nested collections. The script works for the first level, but fails from the second onwards. The error is thrown during the name selector, saying “‘form’ is null or not an object”.

    Any clues?

    • xhalent says:

      @jonk,
      It’s hard to tell without the source script for your situation, but what I might look at is whether the jquery selector used to get the form actually resolves to an element inside a form. If it isn’t inside a form, then I think you’ll get the error. Note that only the first element that matches the selector is used, so it’s important that the first element that satisfies the selector is contained in the form.

      • jonk says:

        Hi, I used the same selector for all of my calls to the function.
        Just to update, I managed to track the problem. Apparently it was special characters in the name of my elements that causes the name selectors to mess up. I escaped all the special characters in the name before passing it into the selector and it works fine.

  21. tjsoftware says:

    Hi.
    I have following script to add new content to my form :

    $().ready(function () {
    $(“#add-phone”).click(function () {
    $.ajax({
    url: ”,
    success: function (data) {
    $(“.phone-numbers”).append(data);
    }
    });
    });
    });

    Where exactly should I place “$.validator.unobtrusive.parseDynamicContent(param);”
    and what would be the param ?
    Unfortunately I don’t know much about JQuery and Java Script.
    Thanks a lot.

  22. While I appreciate the elegance of the code (and I will study it to enhance my jQuery knowledge), you can achieve the same result by placing $.data($(“form”)[0], ‘validator’, false); prior to the call to $.validator.unobtrusive.parse(…) This will clear the parsed validators from the DOM, allowing $.validator.unobtrusive.parse(…) to perform as expected.

    • xhalent says:

      The problem with simply removing the current validators is that it will also remove validators that haven’t been out there through the unobtrusive framework, which is often the case if you have validation that cannot be defined statically but rather depends on the runtime context, for example the values of other fields or some environmental parameter.

  23. Angelo says:

    Thanks. Worked perfectly for what I needed (partial views inside Ajax.BeginForm). If you don’t mind answering – why did you have to write this – why did this work and ‘$.validator.unobtrusive.parse(selector);’ didn’t? I wasted a lot of minutes following solutions that only used ‘$.validator.unobtrusive.parse(selector);’ which other people insisted was working (even on Ajax pages).

    • xhalent says:

      I needed to write this because the unobtrusive library as it stood did not re-parse elements within the form instance twice. Loading whole new forms would be fine, but adding elements to an already loaded form was the problem. Hence why I wrote this.

  24. ssarabando says:

    Just another Thank You Very Much! I was getting disheartened because it was so hard getting such a common thing working. Your extension is a life saver.

  25. ctrlShiftBryan says:

    Thanks!

  26. Lambrecht Geeraerts says:

    Thank you very much for this post. It saved my day.
    I don’t know if anyone mentioned it yet, but I had to make a small change in order to make the extension work with multiple forms. (Performance wise it is better as well I think)

    I changed the line:
    $(“[name='" + elname + "']“).rules(“add”, args);
    Into
    $(selector).find(“[name='" + elname + "']“).rules(“add”, args);

  27. Brett says:

    Great post! I am going to use this implementation for my project. One question though. If i dynamically remove, i.e. $(selector).hide(), an element that I dynamically added and parsed with your extension, would I need to do the reverse of what you did so those hidden elements aren’t validated?

    • xhalent says:

      Hi Brett,
      the easiest way to prevent an element being validate is to disable it. I normally when hiding elements set the disabled attribute to disabled eg $(‘selector’).attr(‘disabled’,’disabled’).hide();

      This has the added adavantage in that disabled elements do not have their values posted to the server.

  28. Brett says:

    One more question for you xhalent! The field validation is working (i.e. input fields receive the default input-validation-error CSS class) when a rule is violated. However, the associated validation messages does not appear. The field-validation-valid CSS class is not being removed from the underlying . Am I missing something?

    • Brett says:

      I should also note that the validation message spans do have the correct ‘for’ attributes set to their associated input id.

      • Brett says:

        Nevermind. I just figured it out myself. In the ‘for’ attribute on the validation message SPAN element, I was using underscores instead of periods between property names (i.e. Collection_Books[0]_Name). I changed the underscores to periods and it worked. =)

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