Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom Client-Side Validation not triggering in ASP.NET Core

I'm adding a custom validation attribute to my code and it works perfectly server side. However, on client side, the method and adapter are registered but the validation function is never called.

I'm working in .NET Core 2.2 using MVC. I've removed the $(Document).Ready wrapper as suggested elsewhere and verified that all of the casing matches.

The following is an MRE

The Test Validation Attribute:

public class TestValidationAttribute : ValidationAttribute, IClientModelValidator
{
    public TestValidationAttribute(string name)
    {
        _name = name;
    }

    private string _name;

    protected override ValidationResult IsValid(Object value, ValidationContext validationContext)
    {
        return new ValidationResult($"{_name} has been validated");
    }

    void IClientModelValidator.AddValidation(ClientModelValidationContext context)
    {
        context.Attributes.Add("data-val", "true");
        context.Attributes.Add("data-val-testvalidation", $"{_name} has been validated client side");
        context.Attributes.Add("data-val-testvalidation-name", _name);
    }
}

The Model:

public class TestModel
{
    [TestValidation("Hello World!")]
    [Display(Name = "Test Value")]
    public string TestValue { get; set; }
}

The View:

<div class="form-group">
    @using (Html.BeginForm())
    {
    <div class="row">
        @Html.LabelFor(m => m.TestValue)
    </div>
    <div class="row">
        @Html.EditorFor(m => m.TestValue)
    </div>
    <div class="row">
        @Html.ValidationMessageFor(m => m.TestValue)
    </div>
    <div class="row">
        <input type="submit" value="Submit" />
    </div>
    }
</div>

The Validation JS:

$(function ($) {
    $.validator.addMethod('testvalidation',
        function (value, element, params) {
            return false;
        });

    $.validator.unobtrusive.adapters.add('testvalidation', ['name'],
        function (options) {
            options.rules['testvalidation'] = { name: options.params['name'] };
            options.messages['testvalidation'] = options.messages;
        });
}(jQuery));

The Generated HTML:

<div class="form-group">
<form action="/" method="post">    <div class="row">
        <label for="TestValue">Test Value</label>
    </div>
    <div class="row">
        <input class="text-box single-line" data-val="true" data-val-testvalidation="Hello World! has been validated client side" data-val-testvalidation-name="Hello World!" id="TestValue" name="TestValue" type="text" value="" />
    </div>
    <div class="row">
        <span class="field-validation-valid" data-valmsg-for="TestValue" data-valmsg-replace="true"></span>
    </div>
    <div class="row">
        <input type="submit" value="Submit" />
    </div>
<input name="__RequestVerificationToken" type="hidden" value="" /></form></div>

        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2019 - WebApplication1 - <a href="/Home/Privacy">Privacy</a>
        </div>
    </footer>


        <script src="/lib/jquery/dist/jquery.js"></script>
        <script src="/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>




    <script src="/lib/jquery-validation/dist/jquery.validate.js"></script>
    <script src="/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
    <script src="/js/testvalidation.js"></script>
    <script src="/js/site.js?v=4q1jwFhaPaZgr8WAUSrux6hAuh0XDg9kPS3xIVq36I0"></script>
like image 604
Lance Avatar asked Nov 22 '25 03:11

Lance


1 Answers

I created a working example based on your question and code.

public class TestModel
{
    [TestValidation("Hello world!", ErrorMessage = "You did not write Hello world!.")]
    [Display(Name = "Test Value")]
    public string TestValue { get; set; }
}
    [AttributeUsage(AttributeTargets.Property)]
    public class TestValidationAttribute : ValidationAttribute, IClientModelValidator
    {
        private object _expectedValue;
        public TestValidationAttribute(object expectedValue)
        {
            _expectedValue = expectedValue;
        }

        public override bool IsValid(object value)
        {
            if (value == null)
            {
                throw new ArgumentNullException("value");
            }
            return Equals(value, _expectedValue);
        }

        public void AddValidation(ClientModelValidationContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
            context.Attributes.Add("data-val", "true");
            context.Attributes.Add("data-val-testvalidation", ErrorMessage);
            context.Attributes.Add("data-val-testvalidation-expectedvalue", _expectedValue.ToString());
        }
    }

Note that, jQuery validation registers its rules before the DOM is loaded. If you try to register your adapter after the DOM is loaded, your rules will not be processed. So wrap it in a self-executing function.

$(function ($) {
    $.validator.addMethod('testvalidation', function (value, element, params) {
        var expectedValue = params.expectedvalue;
        if (!expectedValue) return false;
        var actual = element.value;
        if (actual === expectedValue) return true;
        return false;
    });

    $.validator.unobtrusive.adapters.add('testvalidation', ['expectedvalue'],
        function (options) {

            // Add validation rule for HTML elements that contain data-testvalidation attribute

            options.rules['testvalidation'] = {

                // pass the data from data-testvalidation-expectedvalue to
                // the params argument of the testvalidation method

                expectedvalue: options.params['expectedvalue']
        };
        // get the error message from data-testvalidation-expectedvalue
        // so that unobtrusive validation can use it when validation rule fails
        options.messages['testvalidation'] = options.message;
    });
}(jQuery));

The correct way of loading the scripts would be


@section Scripts{
    @Html.Partial("_ValidationScriptsPartial"); // Or instead like this:
    <!--<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>-->
    <!--<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js">-->
    <script src="~/js/testvalidation.js"></script>
}
like image 193
Dennis VW Avatar answered Nov 24 '25 16:11

Dennis VW



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!