Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS custom validation not firing when changing the model programmatically

Tags:

angularjs

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;
            });
        }
    };
});
like image 650
Jim Cooper Avatar asked Jul 12 '13 17:07

Jim Cooper


2 Answers

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)
        }
    };
});
like image 161
yuxhuang Avatar answered Nov 16 '22 02:11

yuxhuang


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/

like image 35
M'sieur Toph' Avatar answered Nov 16 '22 04:11

M'sieur Toph'