AngularJS custom validation not firing when changing the model programmatically

All we need is an easy explanation of the problem, so here it is.

I have created a custom validator for requiring a date to be in the past. The validation seems to work great when entering the date manually into the field. However, if I enter change the date programmatically (change the model directly as opposed to typing in the field), the validation does not fire.

I believe I am doing the custom validation directive as directed in the documentation. Here is a jsFiddle illustrating the problem. In the fiddle, if you click the “Change date programatically” button, you can see the validation error doesn’t get displayed (but it does if you change it manually). Here is the directive code (also in the fiddle):

myApp.directive('pastDate', function() {
    return {
        restrict: 'A',
        require: '?ngModel',
        link: function (scope, element, attrs, ctrl) {
            ctrl.$parsers.unshift(function (viewValue) {
                var today = new Date();
                today = new Date(today.getFullYear(), today.getMonth(), today.getDate());

                if (new Date(viewValue) < today) {
                    ctrl.$setValidity('pastDate', true);
                    return viewValue;
                }
                ctrl.$setValidity('pastDate', false);
                return undefined;
            });
        }
    };
});

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

There are two ways of the model binding, $parsers controls the pipeline of view-to-model direction, and $formatters controls the pipeline of the model-to-view direction. When you update the model in the controller, the change goes through the $formatters pipeline.

I have updated your code to: this, so it handles both ways.

myApp.directive('pastDate', function() {
    return {
        restrict: 'A',
        require: '?ngModel',
        link: function (scope, element, attrs, ctrl) {
            function validate (value) {
                var today = new Date();
                today = new Date(today.getFullYear(), today.getMonth(), today.getDate());

                if (new Date(value) < today) {
                    ctrl.$setValidity('pastDate', true);
                    return value;
                }
                ctrl.$setValidity('pastDate', false);
                return value;
            }
            ctrl.$parsers.unshift(validate);
            ctrl.$formatters.unshift(validate)
        }
    };
});

Method 2

New answer since angular 1.3 provides $validators property.

Since 1.3, $parsers and $formatters are not supposed to set validity anymore, even if it still possible.

Then your code becomes simpler :

myApp.directive('pastDate', function() {
    return {
        restrict: 'A',
        require: '?ngModel',
        link: function (scope, element, attrs, ctrl) {
            ctrl.$validators.pastDate = function(modelValue) { // 'pastDate' is the name of your custom validator ...
                var today = new Date();
                today = new Date(today.getFullYear(), today.getMonth(), today.getDate());
                return (new Date(modelValue) < today);
            }
        }
    };
});

Updated jsFiddle : http://jsfiddle.net/jD929/53/

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply