Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JQuery 1.5 breaks Compare Validate (JQuery Validate 1.8)

After upgrading to JQuery 1.5 and later 1.5.1, my compare validation fails. I'm using JQuery.Validate 1.7. My ViewModel has the following data annotations:

/// <summary>
/// Gets or sets the full name.
/// </summary>
/// <value>The full name.</value>
[Required]
[Display(Name = "fullname", ResourceType = typeof(Milkshake.Commerce.Model.Resources.Text))]
public string FullName { get; set; }

/// <summary>
/// Gets or sets the email.
/// </summary>
/// <value>The email.</value>
[DataType(DataType.EmailAddress)]
[Display(Name = "email", ResourceType = typeof(Milkshake.Commerce.Model.Resources.Text))]
[Required(ErrorMessageResourceName = "EmailRequired", ErrorMessageResourceType = typeof(Milkshake.Commerce.Model.Resources.ValidationMessages))]
[RegularExpression(@"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*", ErrorMessageResourceType = typeof(Milkshake.Commerce.Model.Resources.ValidationMessages), ErrorMessageResourceName = "EmailInvalid")]
public string Email { get; set; }

/// <summary>
/// Gets or sets the password.
/// </summary>
/// <value>The password.</value>
[DataType(DataType.Password)]
[Display(Name = "password", ResourceType = typeof(Milkshake.Commerce.Model.Resources.Text))]
[Required(ErrorMessageResourceName = "PasswordRequired", ErrorMessageResourceType = typeof(Milkshake.Commerce.Model.Resources.ValidationMessages))]
[ValidatePasswordLengthAttribute(ErrorMessageResourceName = "PasswordLength", ErrorMessageResourceType = typeof(Milkshake.Commerce.Model.Resources.ValidationMessages))]
public string Password { get; set; }

/// <summary>
/// Gets or sets the confirm password.
/// </summary>
/// <value>The confirm password.</value>
[DataType(DataType.Password)]
[Display(Name = "confirmPassword", ResourceType = typeof(Milkshake.Commerce.Model.Resources.Text))]
[Required(ErrorMessageResourceName = "PasswordRequired", ErrorMessageResourceType = typeof(Milkshake.Commerce.Model.Resources.ValidationMessages))]
[Compare("Password", ErrorMessageResourceName = "PasswordsMustMatch", ErrorMessageResourceType = typeof(Milkshake.Commerce.Model.Resources.ValidationMessages))]
public string ConfirmPassword { get; set; }

What ever value I enter, the password fields are never identical.

UPDATE - ASP.NET AntiForgeryToken gets in trouble.

After fooling around in FireBug setting breakpoints, I noticed that in the equalTo validation function, starting on line 1065 in jquery.validate.js, the target element that is found, is not the Password field - but the __RequestVerificationToken that ASP.NET MVC writes when you use the Html.AntiForgeryToken() helper.

So that means we're not even comparing the correct input elements. To work around this issue, I added a dirty hack to the jquery.validate.js file:

// http://docs.jquery.com/Plugins/Validation/Methods/equalTo
equalTo: function (value, element, param) {
    // bind to the blur event of the target in order to revalidate whenever the target field is updated
    // TODO find a way to bind the event just once, avoiding the unbind-rebind overhead
    var target = $(param).unbind(".validate-equalTo").bind("blur.validate-equalTo", function () {
        $(element).valid();
    });

    if ($(target).is("input[type=hidden]") && $(target).attr("name") == "__RequestVerificationToken") {
        var otherElementId = $(element).attr("id");
        var underScoreIndex = otherElementId.indexOf("_");
        otherElementId = otherElementId.substring(0, underScoreIndex + 1);
        otherElementId += $(element).attr("data-val-equalto-other").substring(2);

        target = $("#" + otherElementId);
    }

    return value == target.val();
}

This hack, takes the data-val-equalto-other attribute's value, and mixes it with its own ID, to find the correct input element. Won't work in all cases. But works for me, in the above case.

like image 688
MartinHN Avatar asked Feb 25 '11 12:02

MartinHN


1 Answers

I've found that this is due to an error in jquery.validate.unobtrusive.js

The code in Unobtrusive adds an equalto adapter that tries to find the matching element by its name attribute, using the following code:

element = $(options.form).find(":input[name=" + fullOtherName + "]")[0];

The fullOtherName variable is often (and probably always) namespaced with a period, so the jQuery selector returns too many inputs, and selects the very first one. The period needs to be escaped with .replace(".", "\\."), giving you:

element = $(options.form).find(":input[name=" + fullOtherName.replace(".", "\\.") + "]")[0];

A similar construct exists a few lines lower, and will also need to be fixed (as well as the minified Javascript).

like image 58
Dan Wendorf Avatar answered Sep 28 '22 07:09

Dan Wendorf