Validation for Date Pickers in JavaScript

This is really a minor issues that have been bugging me for a little while.  We are using jQuery UI datepicker controls in our MVC application.  There is one annoying part with these controls.  When you pick a date from the calendar popup, the unobtrusive JavaScript validation we use does not kick in immediately, as it does for text input controls. I found a pretty easy solution and wanted to post it here.  I use onClose callback function that is fired after the calendar is closed to manually run validation.  So, when I hook up date pickers, I do it in the following manner.

            $('.myDatePicker').each(function() {
                $(this).datepicker({
                    onClose: function () {
                        $('#' + $(this).attr('id')).valid();
                    }
                });
            });

Beside automatic validation, I also assign myDatePicker css style to all input controls that accept dates.  This way I just call the code above from ready function in jQuery to hookup all calendar controls in the view.

I can take the whole thing a step further with some automatic templating. I already blogged here about editor templates in ASP.NET MVC.  How about we take our textbox template a step further and have it automatically assign myDatePicker style to all textboxes bound to dates?  Actually, this is pretty easy to do, so here is updated code.

<div class="editor-label">
    @Html.Label(ViewData.ModelMetadata.DisplayName,
                  new Dictionary<string, object>
                      {
                          { "for", ViewData.ModelMetadata.PropertyName }
                      })
</div>
<div class="editor-field">
    @{
        object value = Model;
        var dictionary = new Dictionary<string, object>
         {
             {"id", ViewData.ModelMetadata.PropertyName},
             {"name", ViewData.ModelMetadata.PropertyName},
             {"data-bind", "value: " + ViewData.ModelMetadata.PropertyName},
             {"class", "text-box single-line"}
         };
        if (ViewData.ModelMetadata.ModelType == typeof(DateTime?) || 
            ViewData.ModelMetadata.ModelType == typeof(DateTime))
        {
            dictionary["class"] = "text-box single-line myDatePicker";
            if(Model != null)
            {
                value = ((DateTime)Model).ToString("MM/dd/yyyy");
            }
        }
    }

    @Html.TextBox("", value, dictionary)
</div>
<div class="editor-field">
    @Html.ValidationMessage(ViewData.ModelMetadata.PropertyName,
      new Dictionary<string, object>
          {
                { "data-valmsg-for", ViewData.ModelMetadata.PropertyName }
          })
</div>

 

There are a couple of things you will notice.  One, I am applying some formatting to the date, making sure it is displayed properly. Secondly, I am injecting the style that I will use to hookup date picker. I am using the stadard styles from ASP.NET MVC, like text-box and single-line.  I am also using DisplayName attribute to create a label for the textbox.  If you are not using those, you can specify your own label based on some other information from the model.  The last part of deal with dates is the fact that use we DateTime type in our SQL Server database.  As you know, you cannot put date smaller than 1/1/1753 into such column.  So, it would be nice to have an attribute to support this rule, wouldn’t/t it?

So, I am going to extend validation attribute as well as implement java script side validation.  Here is .NET side

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;

namespace WebDemo.Models
{
    /// <summary>
    /// Implement server and client side validation for 1/1/1753 DateTime smallest value in ]
    /// SQL Server
    /// </summary>
    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
    public class SmallestDateAttribute : ValidationAttribute, IClientValidatable
    {
        /// <summary>
        /// Validates the specified value with respect to the current validation attribute.
        /// </summary>
        /// <param name="value">The value to validate.</param>
        /// <param name="validationContext">The context information about the validation operation.</param>
        /// <returns>
        /// An instance of the <see cref="T:System.ComponentModel.DataAnnotations.ValidationResult" /> class.
        /// </returns>
        protected override ValidationResult IsValid(
            object value, ValidationContext validationContext)
        {
            ValidationResult returnValue = ValidationResult.Success;

            if (value != null)
            {
                if ((DateTime)value < new DateTime(1753,1,1))
                {
                    returnValue = new ValidationResult(
                        ErrorMessageString ?? "Date cannot be less than 1/1/1753");
                }
            }
            return returnValue;
        }

        #region Implementation of IClientValidatable

        /// <summary>
        /// When implemented in a class, 
        /// returns client validation rules for that class.
        /// </summary>
        /// <returns>
        /// The client validation rules for this validator.
        /// </returns>
        /// <param name="metadata">The model metadata.
        /// </param><param name="context">The controller context.</param>
        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
            ModelMetadata metadata, ControllerContext context)
        {
            var rule = new ModelClientValidationRule
            {
                ErrorMessage = 
                    ErrorMessageString ?? "Date cannot be less than 1/1/1753",
                ValidationType = "smallestdate"
            };
            yield return rule;
        }

        #endregion
    }
}

.NET side is pretty much self-explanatory.  On the JavaScript side I have to implement a couple of functions to hook into jQuery unobtrusive validation infrastructure.  “smallestdate“ is the key I am going to use for the this rule.  Of course, because I am using ASP.NET MVC, the data-* attributes to support unobtrusive JavaScript will be injected for me automatically.  Here my custom validation JavScript that I just need to include in my views somewhere, probably just _Layout.cshtml to have it available in all views.

 

$.validator.unobtrusive.adapters.add(
    'smallestdate',
    [],
    function (options) {
        options.rules['smallestdate'] = {};
        if (options.message) {
            options.messages['smallestdate'] = options.message;
        }
    });

$.validator.addMethod('smallestdate', function (value, element, param) {
    if (!value) return true;
    if (Date.parse(value) && Date.parse(value) < (-6847786800000)) {
        return false;
    }
    return true;
});

As you can see above, the key I am using just has to match what I specified in the attribute.  I had to figure out how to express minimum date in JavaScript, and just parse the date value from the textbox.  I do not need parameters this time, although I could have extended the attribute to support a custom minimum date pretty easily.

That is all I wanted to show about dealing with dates in ASP.NET MVC

Leave a Reply

Your email address will not be published. Required fields are marked *