Dynamically changing jQuery unobtrusive validation attributes
Asked Answered
C

4

8

I have a page built in ASP.NET MVC 4 that uses the jquery.validate.unobtrusive library for client side validation. There is an input that needs to be within a range of numbers. However, this range can change dynamically based on user interactions with other parts of the form.

The defaults validate just fine, however after updating the data-rule-range attribute, the validation and message are still triggered on the original values.

Here is the input on initial page load:

<input id="amount" data-rule-range="[1,350]" data-msg-range="Please enter an amount between ${0} and ${1}">

This validates correctly with the message Please enter an amount between $1 and $350 if a number greater than 350 is entered.

After an event fires elsewhere, the data-rule-range is updated and the element looks as such:

<input id="amount" data-rule-range="[1,600]" data-msg-range="Please enter an amount between ${0} and ${1}">

At this point if 500 is entered into the input it will fail validation with the same previous message stating it must be between $1 and $350. I have also tried removing the validator and unobtrusiveValidation from the form and parsing it again with no luck.

$('form').removeData('validator');
$("form").removeData("unobtrusiveValidation");
$.validator.unobtrusive.parse("form");

Is there a clean way to change the validation behavior based on the input attributes dynamically?

Cenac answered 8/7, 2014 at 22:50 Comment(0)
C
7

As Sparky pointed out changing default attributes dynamically will not be picked up after the validation plugin has been initialized. To best work around this without rewiring how we register validated fields and rules, I found it easiest to register a custom adapter in the unobtrusive library:

jQuery.validator.unobtrusive.adapters.add("amount", {}, function (options) {
  options.rules["amount"] = true;
  options.messages["amount"] = function () { return $(options.element).attr('data-val-amount'); };
});

jQuery.validator.addMethod("amount", function (val, el, params) {    
  try {
    var max = $(el).attr('data-amount-max');
    var min = $(el).attr('data-amount-min');
    return val <= max && val >= min;
  } catch (e) {
    console.log("Attribute data-amount-max or data-amount-min missing from input");
    return false;
  }
});

Because the message is a function, it will be evaluated every time and always pick up the latest attribute value for data-val-amount. The downside to this solution is that everytime there is a change we need to change all three attributes on the input: data-amount-min, data-amount-max, and data-val-amount.

Finally here is the input markup on initial load. The only attribute that needs to be present on load is data-val-amount.

<input id="amount" data-val-amount="Please enter an amount between ${0} and ${1}" data-val="true">
Cenac answered 10/7, 2014 at 14:47 Comment(2)
dude, I know this is two years old, but thanks. was banging my head against to wall to get something exactly like this done.Charmian
Stop on. I'm getting the validation messages via ajax calls to some intensive server-side validations. Couldn't change the messages in any other way.Lonergan
B
5

You cannot change rules dynamically by changing the data attributes. That's because the jQuery Validate plugin is initialized with the existing attributes on page load... there is no way for the plugin to auto re-initialize after dynamic changes are made to the attributes.

You must use the .rules('add') and .rules('remove') methods provided by the developer.

See: http://jqueryvalidation.org/rules/

Biology answered 9/7, 2014 at 2:22 Comment(1)
That's what I was worried about. Instead of rewiring our validation to use register rules via script instead of attributes I got around my issue by registering new custom adapters through the unobtrusive library (see my answer). Thank you for confirming this though!Cenac
E
4

you can try this one:

// reset the form's validator
$("form").removeData("validator");

// change the range
$("#amount").attr("data-rule-range", "[1,600]");

// reapply the form's validator
$.validator.unobtrusive.parse(document);
Elutriate answered 3/10, 2014 at 17:22 Comment(2)
FYI, this works, but the validation message is never updated.Oversold
Correction: This does not work properly (error message doesn't get updated, for example). It's best to go through the proper rules() method.Oversold
S
4

charle's solution works! you cannot have model attributes to use it though, I build my inputs like:

      @Html.TextBoxFor(m => Model.EnterValue, new
           {
               @class = "form-control",
               id="xxxx"
               data_val = "true",
               data_val_range = String.Format(Messages.ValueTooBig, Model.ParamName),
               data_val_range_max = 6,
               data_val_range_min = 2,
               data_val_regex_pattern = "^\\d+(,\\d{1,2})?$"
           })

and then in javascript:

 $("form").removeData("validator");
        $("#xxxx").attr('data-val-range-max', 3)
        $("#xxxx").attr('data-val-range-min', 0)
        $.validator.unobtrusive.parse(document);
Skeens answered 5/11, 2015 at 14:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.