ASP.NET MVC RemoteAttribute validation not working - Action not being executed

parliament picture parliament · Dec 26, 2012 · Viewed 8.9k times · Source

I've been pulling my hair out trying to figure out why ValidationController actions are not being triggered.

I have settings enabled in project-wide web.config:

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

I have the following controller:

[OutputCache(Location = OutputCacheLocation.None, NoStore = true)]
public class ValidationController : Controller
{
    private readonly IUserRepository userRepository;

    public ValidationController(IUserRepository userRepository)
    {
        this.userRepository = userRepository;
    }

    public JsonResult IsUserNameAvailable(string username)
    {
        User user = userRepository.Get(u => u.UserName == username);

        if (user == null) return Json(true, JsonRequestBehavior.AllowGet);
        else return Json(false, JsonRequestBehavior.AllowGet);
    }
}

and the following viewmodel:

    [Required(ErrorMessage = "Required *")]
    [StringLength(50, MinimumLength = 4, ErrorMessage = "Please keep the username between four and twenty character.")]
    [Remote("IsUserNameAvailable", "Validation", ErrorMessage = "A user with this username already exists.")]
    [Display(Name = "Username")]
    public string UserName { get; set; }

And I have the following field in my form:

<form id="registerForm">
     ...
     @Html.ValidationMessageFor(m => m.UserName)
     @Html.TextBoxFor(m => m.UserName)
     @Html.LabelFor(m => m.UserName)
</form>

I do ajax form submission and already have server-side validation working perfectly:

$.post("/Account/Register", $('#registerForm').serialize(), function(){
   updateFormWithServerGeneratedErrors();
})

Although the server generated the correct input tag for my field:

<input ... data-val-remote-url="/Validation/IsUserNameAvailable" data-val-remote-additionalfields="*.UserName" data-val-remote="A user with this username already exists." ... >

I can manually hit my action by typing into the url: "/Validation/IsUserNameAvailable?username=SomeName" but Fiddler is NOT showing any requests being made to this url on keypresses or focus changes.

According to this tutorial I don't need to write any javascript to get this working. And yes, I have jquery.validate.js and jquery.validate.unobtrusive.js scripts already loaded by the time I start tinkering with the form.

What is the problem here?

Answer

parliament picture parliament · Jan 6, 2013

Ok I found my answer.

To quote Darin Dimitrov here:

"Unobtrusive validation doesn't work out-of-the-box with dynamically added elements to the DOM - such as for example sending an AJAX request to the server which returns a partial view and this partial view is then injected into the DOM.

In order to make it work you need to register those newly added elements with the unobtrusive validation framework. To do this you need to call the $.validator.unobtrusive.parse on the newly added elements. You should put this code inside the AJAX success handler that is injecting the partial into your DOM."

The framework calls this method one time on page load, the problem in my scenario was that the form itself was a jquery dialog and so even the initial load was "dynamic". I had to register the form elements with the unobtrusive framework on dialog load:

$.validator.unobtrusive.parse('#registerForm');  

My ajax calls would also return and replace the form like so if it did not validate on the server-side:

registerDialog.empty().html(result.viewResult);

so I had to call parse() on the success callback as well to make sure validation continues to work after an ajax submit.