Performing client-side validation in MVC4 View against ViewModel

usr-local-ΕΨΗΕΛΩΝ picture usr-local-ΕΨΗΕΛΩΝ · Mar 28, 2013 · Viewed 7.6k times · Source

I would like to know what is the best approach to handle client-side, javascript or jQuery driven validation of MVC4 fields against attributes placed on a ViewModel's fields.

First, let's pick the example. A login creation screen for Administrators shown the first time the application starts (just not to say to the site owner "use admin/admin as login the first time").

ViewModel:

public class AdministratorViewModel : AbstractViewModel
{
    [Required]
    [Display(ResourceType = typeof(ManageAdminsViewModelResources), Name = "lblUsername")]
    public string Username { get; set; }

    [Required]
    [Display(ResourceType = typeof(ManageAdminsViewModelResources), Name = "lblEmailAddress")]
    [EmailAddress]
    public string EmailAddress { get; set; }

    [Required]
    [Display(ResourceType = typeof(ManageAdminsViewModelResources), Name = "lblPassword")]
    [AdminPassword]
    public string Password { get; set; }

    [Required]
    [Display(ResourceType = typeof(ManageAdminsViewModelResources), Name = "lblPasswordConfirm")]
    [Compare("Password")]
    public string PasswordConfirm { get; set; }

    [Display(ResourceType = typeof(ManageAdminsViewModelResources), Name = "lblLastLogin")]
    public DateTime? LastLogin { get; set; }

    [Display(ResourceType = typeof(ManageAdminsViewModelResources), Name = "lblPasswordExpiry")]
    public DateTime? PasswordExpiry { get; set; }

    [Display(ResourceType = typeof(ManageAdminsViewModelResources), Name = "lblBlocked")]
    public bool Blocked { get; set; }
}

Partial view (only a few fields needed when creating the first admin)

@using (Html.BeginForm())
{
    @Html.ValidationSummary(false)
    @Html.AntiForgeryToken()

    <fieldset>
        <legend>@ManageAdminsViewResources.legendCreateAdmin</legend>

        <div class="desktoptile">
            @Html.LabelFor(m=>m.Username)
            @Html.EditorFor(m => m.Username)
            @Html.ValidationMessageFor(m => m.Username)
        </div>

        <div class="desktoptile">
            @Html.LabelFor(m=>m.Password)
            @Html.PasswordFor(m => m.Password)
            @Html.ValidationMessageFor(m => m.Password)
        </div>

        <div class="desktoptile">
            @Html.LabelFor(m=>m.PasswordConfirm)
            @Html.PasswordFor(m => m.PasswordConfirm)
            @Html.ValidationMessageFor(m => m.PasswordConfirm)
        </div>

        <div class="desktoptile">
            @Html.LabelFor(m=>m.EmailAddress)
            @Html.EditorFor(m => m.EmailAddress)
            @Html.ValidationMessageFor(m => m.EmailAddress)
        </div>

        <input type="submit" value="@ManageAdminsViewResources.btnCreate"/>
    </fieldset>
}

Controller

    [AllowAnonymous]
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult CreateFirst(AdministratorViewModel viewModel)
    {
        if (!ModelState.IsValid) return View(viewModel);
        [...................]

Currently

If I enter an invalid email address, an empty password, etc. in the form and hit Submit I'm correctly notified of the errors. Ok, let's go on

What I want

Since I'm doing a Metro-stylish design, I would like that every time the user unfocuses a text box validation for that field occurs.

Writing hard-coded jQuery fragments is not the best option. I would like a data-driven approach, possibly embedded in MVC4 which I'm currently learning.

So, given a ViewModel with standard and custom attributes (for which, no matter what, a little Javascript is required, think about the Org.Zighinetto.AdminPasswordAttribute that checks password complexity), how do I enforce client-side validation the most unobtrusive way, without specifying client-side tags on each and every html tag and writing the least possible amount of code?

Is still there any secret in ASP.NET MVC 4 validation that I have to unhide?

Answer

mattytommo picture mattytommo · Mar 28, 2013

Well, you would have to invoke jQuery validate using jQuery (because it's written in jQuery :))

You could add a global event for your inputs, then invoke it on the blurred element. Something like:

$("input").blur(function () {
    $(this).valid();
});