ASP.NET MVC 4 - Clientside Validation Not Working

G2Mula picture G2Mula · Mar 20, 2013 · Viewed 12.3k times · Source

I am using Visual Studio 2012 and I cannot get a custom attribute client side logic to work to reproduce at a smaller scale, I created a new MVC 4 project I created the following model and Attribute that will never validate

public class MyModel
{
    public int Id { get; set; }
    [Required]
    public string LastName { get; set; }
    [NeverValid(ErrorMessage="Serverside Will Never Validate")]
    public string FirstName { get; set; }
}

public class NeverValidAttribute : ValidationAttribute, IClientValidatable
{
    public override bool IsValid(object value)
    {
        return false;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        return new ValidationResult(this.ErrorMessage, new[] { validationContext.MemberName });
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        yield return new ModelClientValidationRule
        {
            ErrorMessage = this.ErrorMessage,
            ValidationType = "nevervalid"
        };
    }
}

I then have the following actions added to the HomeController

public ActionResult Index()
{
    return View(new MyModel());
}

[HttpPost]
public ActionResult Index(MyModel model)
{
    if (!ModelState.IsValid)
    {
        // Will Always Be Invalid
    }

    return View(model);
}

There is also a javascript file called nevervalid.js

$(function () {
    $.validator.addMethod("nevervalid", function () {
        return false;
    }, "Clientside Should Not Postback");

    $.validator.unobtrusive.adapters.addBool("nevervalid");
});

and the Index View

@model CustomAttribute.Models.MyModel

@{
    ViewBag.Title = "Home Page";
}

@using (Html.BeginForm())
{
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>MyModel</legend>

        @Html.HiddenFor(model => model.Id)

        <div class="editor-label">
            @Html.LabelFor(model => model.LastName)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.LastName)
            @Html.ValidationMessageFor(model => model.LastName)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.FirstName)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.FirstName)
            @Html.ValidationMessageFor(model => model.FirstName)
        </div>

        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
}

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
    @Scripts.Render("~/Scripts/nevervalid.js")
}

The relevant areas in my web.config look like this

<appSettings>
    <add key="webpages:Version" value="2.0.0.0" />
    <add key="webpages:Enabled" value="false" />
    <add key="PreserveLoginUrl" value="true" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
</appSettings>

when the page loads, the following files are loaded (got this from network tab under chrome's F12)

http://localhost:7440/
http://localhost:7440/Content/site.css
http://localhost:7440/Scripts/modernizr-2.5.3.js
http://localhost:7440/Scripts/jquery-1.7.1.js
http://localhost:7440/Scripts/jquery.unobtrusive-ajax.js
http://localhost:7440/Scripts/jquery.validate.js
http://localhost:7440/Scripts/jquery.validate.unobtrusive.js
http://localhost:7440/Scripts/nevervalid.js

and my custom attribute adds relevant looking data- stuff to the first name input like so...

    <input class="text-box single-line valid" data-val="true" data-val-nevervalid="Serverside Will Never Validate" id="FirstName" name="FirstName" type="text" value="">

so, I ask you, why oh why does this thing have to postback to do serverside validation while I have some perfectly looking javascript code here? do I have to sacrifice some animal on a moonless night on top of a hill somewhere?

Answer

Charlino picture Charlino · Mar 20, 2013

I'm wondering what would happen if you altered nevervalid.js so that the code executes before the document is ready.

My basic thinking is that the unobtrusive validation works by parsing the document when the dom is ready... but if you're loading your custom validation stuff at the same time / afterwards, it's not going to know what to do when it comes accross you custom validation data- attribute.

Anyway, try changing nevervalid.js to simply be:

(function($) {
    $.validator.addMethod("nevervalid", function () {
        return false;
    }, "Clientside Should Not Postback");

    $.validator.unobtrusive.adapters.addBool("nevervalid");
})(jQuery);