logo

AngularJS jQuery UI Datepicker Directive

HTML5 has some great features - input[date] for example - which are well supported by AngularJS.

BUT, take a look at caniuse.com and things aren't quite so rosy.

I recently needed a datepicker for an AngularJS project, and chose jQuery UI's Datepicker Widget as a fallback for HTML5's the native input[date].

AngularJS Modernizr Provider provider/modernizr.js

I'm using Modernizr for feature detection. To ensure the directive is testable, I wanted to be able to inject Modernizr...

... firstly, I create an IIFE closure to prevent pollution of the global namespace:

(function () {

    'use strict';

    angular
        .module('ocean-internet')
        .provider('modernizr', modernizr);

    modernizr.$inject = [];

    // modernizr() Here

})();

... and now for the AngularJS Modernizr Provider:

function modernizr() {

    this.$get = function () {
        return Modernizr || {};
    };
}

... there we have our sparkly new injectable/testable Modernizr Provider.

AngularJS Datepicker Directive directive/jqDatepicker.js

Next for the jQuery UI Datepicker Directive...

... I create another IIFE closure:

(function () {
    'use strict';

    angular
        .module('ocean-internet')
        .directive('jqDatepicker', jqDatepicker);

    // jqDatepicker() Here

})();

... inject my Modernizr Provider:

jqDatepicker.$inject = ['$filter', 'modernizr'];  

... create the jqDatepicker() function:

function jqDatepicker($filter, modernizr) {

    return {
        restrict: 'A',
        require:  'ngModel',
        link:     link
    };

    // link() Here
}

... the link() function:

function link(scope, element, attrs, ngModelCtrl) {

    var settings = {
            dateFormat:  'dd/mm/yy', // My chosen display format
            changeMonth: true,       // I wanted a month selector
            onSelect:    onSelect    // I'll use this to update the model
        };



    // Check for native input[date] availability
    if (!modernizr.hasOwnProperty('inputtypes') || !modernizr.inputtypes.date) {

        if(attrs.min) {
            settings.minDate = new Date(attrs.min);
        }

        if(attrs.max) {
            settings.maxDate = new Date(attrs.max);
        }

        // I wanted a year selector if minDate and maxDate are set
        if(settings.hasOwnProperty('minDate') && settings.hasOwnProperty('maxDate')) {

            var minYear = settings.minDate.getFullYear(),
                maxYear = settings.maxDate.getFullYear();

            if(minYear !== maxYear) {

                var range = minYear + ':' + maxYear;

                settings.changeYear = true;
                settings.yearRange  = range;
            }
        }

        element.datepicker(settings);
    }                

    // onSelect() Here
}

... and finally, the onSelect() function:

function onSelect(date) {

    scope.$apply(function () {

        var parsedDate = jQuery.datepicker.parseDate(settings.dateFormat, date),
            mysqlDate  = $filter('date')(parsedDate, 'yyyy-MM-dd');

        ngModelCtrl.$setViewValue(mysqlDate);
    });
}

jqDatepicker Directive

To use the directive:

...
    <!-- Styles -->
    <link rel="stylesheet" href="jquery-ui-theme.css" type="text/css" />
...
    <!-- Dependencies -->
    <script src="jquery.js"></script>
    <script src="jquery-ui.js"></script>
    <script src="modernizr.js"></script>
...
    <!-- MyApp -->
    <script src="providers/modernizr.js"></script>
    <script src="directives/jqDatepicker.js"></script>
...
<input type="date" min="2014-01-01" max="2014-12-31" data-jq-datepicker />  

Download provider/modernizr.js & directive/jqDatepicker.js from my GitHub Repository