jqueryvalidationdevexpressasp.net-mvc-5devexpress-mvc

jQuery-validate custom rules cause other invalid fields to be ignored


Apologies, it's probably something really silly but I can't work this out.

I am using C#, ASP.NET MVC 5 and have the following in my model:

    /// <summary>
    /// A none-nullable datetime representing the start of the meeting.
    /// </summary>
    [Display(Name = "Date")]
    [UIHint("Date")]
    public System.DateTime Start { get; set; }

    /// <summary>
    /// The time aspect of the Meeting Start is split off from the date
    /// portion. This property allows us to correctly display a separate
    /// date and time but uses the same underlying property.
    /// </summary>
    [Display(Name = "Time")]
    [UIHint("Time")]
    public System.DateTime StartTime
    {
        get
        {
            return Start;
        }
        set
        {
            Start = System.DateTime.ParseExact(Start.ToString("dd/MM/yyyy ") + value.ToString("HH:mm"), "dd/MM/yyyy HH:mm", System.Globalization.CultureInfo.InvariantCulture);

        }
    }

    /// <summary>
    /// The end time of the meeting.
    /// Since the system does not allow meetings to span multiple days this
    /// will be married up with the date part of Start when we save.
    /// </summary>
    [Display(Name = "Planned finish")]
    [UIHint("Time")]
    public System.DateTime Finish { get; set; }

    /// <summary>
    /// Set after the meeting to display actual start time.
    /// </summary>
    [Display(Name = "Started")]
    [UIHint("Time")]
    public System.DateTime Started { get; set; }

    /// <summary>
    /// Set after the meeting to display actual finished time.
    /// </summary>
    [Display(Name = "Finished")]
    [UIHint("Time")]
    public System.DateTime Finished { get; set; }

    [Required]
    [Display(Name ="Primary contact")]
    public string Contact { get; set; }

    [Required]
    [Display(Name = "Secondary contact")]
    public string Contact2 { get; set; }

In the browser this works fine with both contacts (i.e. the form doesn't submit unless they both have a value.) I'm including both the jquery-validate.js library and the microsoft jquery-validate-unobtrusive.js so these seem to be working fine.

Part of my problem is the date and time pickers (Noting the UIHint attributes) which use a custom editor template that basically creates a DevExpress date time picker (for convenience later I have added two classes depending on whether the picker is for a Date or Time input (devexpress-date devexpress-time). This works okay, and the validation properties such as required etc. come through fine. But... since they are considered date and time inputs they fail validation based on the format (taking the date example it only accepts the format yyyy-MM-dd. Now I've got around this issue by following the example here: Custom date format with jQuery validation plugin. And have made a similar one for times.

I have laid all this out in a separate js file I include straight after both of the validation libraries (using a bundle). This all seems okay conceptually (I got this guidance from: http://www.devtrends.co.uk/blog/the-complete-guide-to-validation-in-asp.net-mvc-3-part-2). But... here is where things get a bit tricky.

For clarification my CustomValidators.js contains:

(function ($) {
$.validator.addMethod("date", function (value, element, params) {
    if (!this.optional(element)) {
        var bits = value.match(/([0-9]+)/gi), str;
        if (!bits)
            return this.optional(element) || false;
        str = bits[1] + '/' + bits[0] + '/' + bits[2];
        alert("testing date " + value);
        return this.optional(element) || !/Invalid|NaN/.test(new Date(str));
    }
    return true;
});
$.validator.unobtrusive.adapters.addBool("date");

$.validator.addMethod('time', function (value, element) {
    value = value.trim();
    alert("Testing time: " + value);
    if (value.length != 5 || value.indexOf(":") == -1) {
        return false;
    }
    var parts = value.split(":");
    var hour = ParseInt(parts[0]);
    var minutes = ParseInt(parts[1]);
    if (isNaN(hour) || isNaN(minutes)) {
        return false;
    }
    if (hour < 0 || hour > 23) {
        return false;
    }
    if (minutes < 0 || minutes > 59) {
        return false;
    }
    return this.optional(element) || true;
});

$.validator.unobtrusive.adapters.addBool("time");

}(jQuery));

** The problem **

Because the devexpress inputs register as datetimes it tries to validate using the standard datetime rules (returning false and giving me a lovely validation summary warning about invalid dates).

To get around this I have tried on $(document).ready() to bind any inputs with the devexpress-date and devexpress-time properties to have different rules:

        $(document).ready(function () {
        if ($("form").length > 0)
        {
            $("form").each(function () {
                $(this).validate();
                $(".devexpress-date input").each(function () {
                    $(this).addClass("devexpress-date");
                    $(this).rules("add", "date");
                });
            });

            $("form").each(function () {
                $(this).validate();
                $(".devexpress-time input").each(function () {
                    $(this).addClass("devexpress-time");
                    $(this).rules("add", "time");
                    $(this).rules("remove", "date");
                });
            });
          }
        }

You'll notice some alerts in my validator functions, so when I try to submit the form I get:

Trying Date: 08/04/2017
Trying Time: 00:00

I don't get an alert for the other three time boxes on the page and even if the Contact or Contact2 fields are blank the form submits fine. It's like I've broken the validation after these elements but I don't understand why.

Please can someone explain to me why? The application will have numbers, text, date and time inputs all with similar rules so the ideal solution is to make the custom rules work so that I can run the aforementioned rules adding in document.ready() on any page that has a form and for it to basically automatically work. I thought I was close but the fact that the other validations now don't work makes me feel like I'm a long way off my intention, so all help is appreciated.

Thanks.


Solution

  • I ran into this with version 1.14. Upgrading jquery.validate.js to 1.17 and jquery.validate.unobtrusive.js to 3.2.6 fixed the problem.