Search code examples
jqueryasp.net-coreasp.net-core-mvcmodel-validation

RangeAttribute for DateTime validation not working in MVC web app


Using the RangeAttribute with a type of DateTime to to specify a minimum and maximum value for a date property does not work. A question on how to work around this has been asked here, with the highest-voted solution being:

There is no need to disable jQuery date validation (and that is likely to cause other issues). You just need to override the range method of the $.validator.

By default, it works with numeric values (and then falls back to a string comparison), so you can add the following script (after jquery.validate.js and jquery.validate.unobtrusive.js, but not wrapped in $(document).ready

$.validator.methods.range = function(value, element, param) {
    if ($(element).attr('data-val-date')) {
        var min = $(element).attr('data-val-range-min');
        var max = $(element).attr('data-val-range-max');
        var date = new Date(value).getTime();
        var minDate = new Date(min).getTime();
        var maxDate = new Date(max).getTime();
        return this.optional(element) || (date >= minDate && date <= maxDate);
    }
    // use the default method
    return this.optional( element ) || ( value >= param[ 0 ] && value <= param[ 1 ] );
};


Can someone please help me understand how to implement this solution? (My HTML & Javascript knowledge is embarrassingly poor, so any help is appreciated.)


From the description of the solution, I would assume I want this script to end up at the bottom of the generated HTML, so I have tried adding it to my _ValidationScriptsPartial.cshtml, so I now have:

<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
<script type="text/javascript">
    $.validator.methods.range = function(value, element, param) {
    if ($(element).attr('data-val-date')) {
        var min = $(element).attr('data-val-range-min');
        var max = $(element).attr('data-val-range-max');
        var date = new Date(value).getTime();
        var minDate = new Date(min).getTime();
        var maxDate = new Date(max).getTime();
        return this.optional(element) || (date >= minDate && date <= maxDate);
    }
    // use the default method
    return this.optional( element ) || ( value >= param[ 0 ] && value <= param[ 1 ] );
};
</script>

The script now appears at the bottom of the generated HTML, however it doesnt seem to affect the validation error that appears on the form.


Solution

  • The script now appears at the bottom of the generated HTML, however it doesnt seem to affect the validation error that appears on the form.

    Based on the code that you provided, I suspect that it might not reach into if ($(element).attr('data-val-date')) code block, which casue your customized client-side validation function does not work as expected.

    Please check the source code of the corresponding element and make sure it has data-val-date attribute.

    Besides, if the element does not include data-val-date="true" as below, you can modify the code like below to make the code snippet (within if condition block) could be reached while the function is triggered by that specific element.

    StartDateTime property

    [Range(typeof(DateTime), "1/1/2020", "2/29/2020")]
    public DateTime StartDateTime { get; set; }
    

    Html source code of that specific element

    enter image description here

    Modified code to check if current element is that specific element with if $(element).attr('id') == "StartDateTime")

    @section Scripts {
        @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
    
    <script>
        $.validator.methods.range = function (value, element, param) {
    
            //console.log(param);
    
            if ($(element).attr('id') == "StartDateTime") {
                var min = $(element).attr('data-val-range-min');
                var max = $(element).attr('data-val-range-max');
                var date = new Date(value).getTime();
                var minDate = new Date(min).getTime();
                var maxDate = new Date(max).getTime();
                console.log(minDate + " | " + maxDate + " | " + date);
    
                return this.optional(element) || (date >= minDate && date <= maxDate);
            }
            // use the default method
            return this.optional(element) || (value >= param[0] && value <= param[1]);
        }
    </script>
    }