Question

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?

Was it helpful?

Solution

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.

OTHER TIPS

You need to change your JsonResult method parameter name from

public JsonResult IsUserNameAvailable(string username)

to

public JsonResult IsUserNameAvailable(string UserName)

it should be same as property name its case-sensitive.

The problem why validation function is not being fired is that this code

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);
}

should be added to the AccountController, not the ValidationController. Remove ValidationController class and add the IsUserNameAvailable method to your AccountController class.

Also change the line in the model, it should be something like this

[System.Web.Mvc.Remote("IsUserNameAvailable", "Account")]

That will solve the issue.

It seems you forget to specify the binding Action for the form. I tried this below code and it works

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

or simply:

@using(Html.BeginForm()) {
     @Html.ValidationMessageFor(m => m.UserName)
     @Html.TextBoxFor(m => m.UserName)
     @Html.LabelFor(m => m.UserName)
}

I had kind of the same issue. Simply change this:

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);
}

to:

public JsonResult IsUserNameAvailable(string Username)
{
    User user = userRepository.Get(u => u.UserName == Username);
    if (user == null) 
    {
        return Json(true, JsonRequestBehavior.AllowGet);
    }
    return Json(false, JsonRequestBehavior.AllowGet);       
}

username to UserName which is your

public string UserName { get; set; }

I think it's case sensitive.

Hope it will help :)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top