Material: mdDatepicker does not remove md-datepicker-invalid class when value is set after a delay

Created on 4 Dec 2015  路  13Comments  路  Source: angular/material

In 1.0.0-rc6 the md-datepicker-invalid class is not removed from the mdDatepicker when the field is required and when the model value is set after a delay (after an asynchronous call in a real-world scenario). It has to do with the required validation error state not being handled correctly in the DatePickerCtrl updateErrorState function.

Here is a CodePen to demonstrate the issue:
http://codepen.io/bgoerdt/pen/rxNmqN

urgent

Most helpful comment

Here's a directive I threw together as a workaround until there's a proper fix. Note this also fixes #5938.

function datepickerValidationFix() {
    return {
        restrict: 'A',
        require: 'mdDatepicker',
        link: function (scope, element, attrs, mdDatepickerCtrl) {
            // Fix to run validation when a datepicker's minDate changes
            // Bug #5938
            mdDatepickerCtrl.$scope.$watch(function () { return mdDatepickerCtrl.minDate; }, function () {
                if (mdDatepickerCtrl.dateUtil.isValidDate(mdDatepickerCtrl.date)) {
                    mdDatepickerCtrl.updateErrorState.call(mdDatepickerCtrl);
                }
            });

            // Fix to clear error state when setting date programatically from null
            // Bug #6086
            mdDatepickerCtrl.$scope.$watch(function () { return mdDatepickerCtrl.date; }, function (newVal, oldVal) {
                if (newVal && !oldVal) {
                    mdDatepickerCtrl.updateErrorState.call(mdDatepickerCtrl);
                }
            });
        }
    };
}

All 13 comments

Update: this is still happening in 1.0.0-rc7. Same CodePen as above.

I'm having the same issues in 1.0.0-rc7 as well.

This is still occurring in version 1.0.1. Can anyone take a look?

+1

+1

+1

Here's a directive I threw together as a workaround until there's a proper fix. Note this also fixes #5938.

function datepickerValidationFix() {
    return {
        restrict: 'A',
        require: 'mdDatepicker',
        link: function (scope, element, attrs, mdDatepickerCtrl) {
            // Fix to run validation when a datepicker's minDate changes
            // Bug #5938
            mdDatepickerCtrl.$scope.$watch(function () { return mdDatepickerCtrl.minDate; }, function () {
                if (mdDatepickerCtrl.dateUtil.isValidDate(mdDatepickerCtrl.date)) {
                    mdDatepickerCtrl.updateErrorState.call(mdDatepickerCtrl);
                }
            });

            // Fix to clear error state when setting date programatically from null
            // Bug #6086
            mdDatepickerCtrl.$scope.$watch(function () { return mdDatepickerCtrl.date; }, function (newVal, oldVal) {
                if (newVal && !oldVal) {
                    mdDatepickerCtrl.updateErrorState.call(mdDatepickerCtrl);
                }
            });
        }
    };
}

I exported your code into a directive decorator. Moreover, I removed the condition from second $watch because I was still getting inconsistent red underlines (now they're gone).
_Mind the code is in ES2016 so you might need to modify it to make it work without a transpiler._

/**
 * https://github.com/angular/material/issues/6086
 */
function fixMdDatepicker($provide) {
    $provide.decorator('mdDatepickerDirective', function ($delegate) {
        const mdDatepicker = $delegate[0];
        const originalLink = mdDatepicker.link;

        mdDatepicker.compile = function () {
            return function fixMdDatepickerLink(scope, element, attr, controllers) {
                originalLink.apply(this, arguments);

                const mdDatePickerCtrl = controllers[1];

                // https://github.com/angular/material/issues/5938
                mdDatePickerCtrl.$scope.$watch(() => mdDatePickerCtrl.minDate, () => {
                    if (mdDatePickerCtrl.dateUtil.isValidDate(mdDatePickerCtrl.date)) {
                        mdDatePickerCtrl.updateErrorState.call(mdDatePickerCtrl);
                    }
                });

                // https://github.com/angular/material/issues/6086
                mdDatePickerCtrl.$scope.$watch(() => mdDatePickerCtrl.date, (newVal, oldVal) => {
                    mdDatePickerCtrl.updateErrorState.call(mdDatePickerCtrl);
                });
            };
        };

        return $delegate;
    });
}

I didn't know how to apply the directive, I'm leaving here the code is working for me:

var WebApp = angular.module('WebApp', []);

WebApp.directive('datepickerValidationFix', function () {
    return {
        restrict: 'A',
        require: 'mdDatepicker',
        link: function (scope, element, attrs, mdDatepickerCtrl) {
            // Fix to run validation when a datepicker's minDate changes
            // Bug #5938
            mdDatepickerCtrl.$scope.$watch(function () { return mdDatepickerCtrl.minDate; }, function () {
                if (mdDatepickerCtrl.dateUtil.isValidDate(mdDatepickerCtrl.date)) {
                    mdDatepickerCtrl.updateErrorState.call(mdDatepickerCtrl);
                }
            });

            // Fix to clear error state when setting date programatically from null
            // Bug #6086
            mdDatepickerCtrl.$scope.$watch(function () { return mdDatepickerCtrl.date; }, function (newVal, oldVal) {
                mdDatepickerCtrl.updateErrorState.call(mdDatepickerCtrl);
            });
        }
    };
});

hope it would help someone.

But it's exactly the same code as above, isn't it? What's the difference?

yes @mmiszy, it is. However I tried to show how to apply a directive for the main angular module, since I'm new on angular, was hard for me find how to apply it.
If you think it wont help anyone, I will erase my comment.

@ngraef solution worked.
Here's an example
http://codepen.io/dancosta93/pen/YWKrEw

The solution is simple...don't need to add a directive.
Instead of required, just use ng-required, that's it.

Was this page helpful?
0 / 5 - 0 ratings