MVC CompareAttribute validation problem with nested models

Today, I ran into a problem a weird problem with MVC CompareAttribute when used with a complex model.   After some investigation, I found a bug in Microsoft MVC’s jQuery.validate.unobtrusive.js.  Let me describe what happens:

For simplicity, let’s say I have a View Model that has a nested view model:

public class HomeViewModel

{

public SignUpModel SignUpModel { get; set; }

}

public class SignUpModel

{

[Required]

[DataType(DataType.EmailAddress)]

public string Email { get; set; }

[Required]

public string Password { get; set; }

[Compare("Password")]

public string ConfirmPassword { get; set; }

}

Then try to create a view based on the view model:

@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
<fieldset>
<legend>HomeViewModel</legend>
Email: @Html.TextBoxFor(m => m.SignUpModel.Email)
@Html.ValidationMessageFor(m => m.SignUpModel.Email)<br />
Password: @Html.PasswordFor(m => m.SignUpModel.Password)
@Html.ValidationMessageFor(m => m.SignUpModel.Password)<br />
Confirm: @Html.PasswordFor(m => m.SignUpModel.ConfirmPassword)
@Html.ValidationMessageFor(m => m.SignUpModel.ConfirmPassword)
<p>
<input type=”submit” value=”Create” />
</p>
</fieldset>
}

So far, everything looks pretty simple.  However if you try to submit this form with proper inputs, you will stuck with an Confirm Password error.

Weird Validation Error from MvcCompareAttribute

So what is causing it?  It turns out in jQuery.validate.unobtrusive.js, there is a problem when selecting the form input.  When your view model is nested, MVC generates the input name by concatenating the property names with a dot.  In our example, it will be <input type=”password” name=”SignUpModel.Password”>.   If you look at the code below, you will see the selector in red:

adapters.add(“equalto”, ["other"], function (options) {
var prefix = getModelPrefix(options.element.name),
other = options.params.other,
fullOtherName = appendModelPrefix(other, prefix),
element = $(options.form).find(“:input[name=" + fullOtherName + "]“)[0];
setValidationValues(options, “equalTo”, element);
});

If break at this javascript line, you will see the selector will try to resolve “:input[name=SignUpModel.Password]” but gets the wrong Html Element!  In fact, the selector ends up getting the first form element (email) and hence causing the validation to fail.  What you can do is to modify the line by adding the single quotation to avoid the problem.

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

When I saw this problem today, I am using jQuery version 1.5.1.  I don’t know if this is a jQuery bug that is fixed in newer version.  It would be great if someone can let me know!

By Raymond Tsang

Tagged with: , ,
Posted in Technical
One comment on “MVC CompareAttribute validation problem with nested models
  1. Aaron Bynum says:

    You, sir, are a scholar and a gentleman. This is EXACTLY the situation I faced. Your fix worked and saved me from some major headaches

Leave a Reply