15

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;
            });
        }
    };
});

2 Answers 2

19

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)
        }
    };
});
Sign up to request clarification or add additional context in comments.

4 Comments

@Terry yah, unfortunately you're going to miss the cakes in the office today.
Thanks for the explanation, this makes a lot more sense now that I understand the difference between $parsers and $formatters.
Urgh, great answer. Was stuck on this for quite some time today. Only used the $parsers (view -> model) direction.
I was stuck for a day on this too and had to update a public directive I was using. Thank you!
11

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/

7 Comments

Using $validators, how can i access other scope properties or other fields in my form?
You can access all needed elements by using the scope, element, attrs parameters of the link function. As you are inside this function, these elements are available.
However, you cannot access other form fields. At least not in a simply way. But, theorically, the validation of a field should not depend on other fields properties. If you still need to, you should set a $watch of the source field property in the parent directive (the one with all the form) and, within, set a custom property in the field you need to validate. Then your validator will be able to see this custom property, as long as it is its own. Hope being clear enough...
1) Inside ctrl.$validators.myValidation, the scope, element, attrs are undefined. 2) Based on your opinion, if my field validation depends on another field, then i should use $parsers and formatters ??
Give me a fiddle with your code and a minimal explanation of your goal. I cannot tell without seeing the existing code.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.