0

Created a custom validation directive that invalidates an input when anything else than a number or space is input. When I change the value programmatically to something that should pass validation, the validation state is not changed.

Check this JSFIDDLE and see for yourself. Any ideas?

<div ng-app="test" ng-controller="Ctrl">
<form name="form">
    <input custom-validation type="text" ng-model="box.text" name="text" />
</form>
<button ng-click="change()">Change to numbers only</button> 
Why doesn't changing to numbers only pass the validation?
</div>

angular.module('test', []);
angular.module('test').directive('customValidation', function () {
    'use strict';
    return {
        require: '?ngModel',
        link: function (scope, element, attrs, ngModelCtrl) {

            ngModelCtrl.$parsers.push(function removeIllegalCharacters(viewValue) {

                if(viewValue === undefined){
                    ngModelCtrl.$setValidity('numbersAndSpacesOnly', true); //empty value is always valid
                } else {
                    var clean = viewValue.replace(/[^0-9 ]+/g, '');
                    if (viewValue === clean) {
                        ngModelCtrl.$setValidity('numbersAndSpacesOnly', true);
                    } else {
                        ngModelCtrl.$setValidity('numbersAndSpacesOnly', false);
                    }
                }

                return viewValue;
            });

        }
    };
});

angular.module('test').controller('Ctrl', function ($scope, $timeout) {
    $scope.change = function () {
        $scope.box.text = '12345';
    }
});
2
  • 1
    You have to add the validator to the $formatters array too. Commented Jul 30, 2014 at 12:45
  • Alright thanks, i'll look into it! If you'd like to give an explanation aswell that'd be great. Commented Jul 30, 2014 at 12:47

1 Answer 1

3

ngModel uses 2 pipelines (arrays) of code for validation:

  1. The $parsers array has functions to apply to the view value when it changes from the user; each function is called with the value returned form the previous, the first function is called with the view value and the return of the last function is written in the model. This is commonly used to validate and convert user input (e.g. from the text of an input type="text" to a number).

  2. The $formatters array works similarly, but in the opposite direction. It receives the model value and transforms it, with the return of the last function being the new view value.

Functions in both pipelines may choose to call ngModel.$setValidity() to alter the validity state of the ngModel.

For the scope of this question: in order to validate the model value, you have to use the $formatters similarly to the $parsers you are already using:

angular.module('test').directive('customValidation', function () {
    'use strict';
    return {
        require: '?ngModel',
        link: function (scope, element, attrs, ngModelCtrl) {

            function removeIllegalCharacters(value) {
                if(value === undefined){
                    ngModelCtrl.$setValidity('numbersAndSpacesOnly', true); //empty value is always valid
                } else {
                    var clean = value.replace(/[^0-9 ]+/g, '');
                    if (value === clean) {
                        ngModelCtrl.$setValidity('numbersAndSpacesOnly', true);
                    } else {
                        ngModelCtrl.$setValidity('numbersAndSpacesOnly', false);
                    }
                }

                return value;
            }

            ngModelCtrl.$parsers.push(removeIllegalCharacters);
            ngModelCtrl.$formatters.push(removeIllegalCharacters);
        }
    };
});
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the explanation. One thought: could I just as well watch the model property and set the validity based on that? What is the advantage of using parsers and formatters in this scenario?
I think ngModel already watches the model value and triggers the $formatters pipeline; so watching the model value yourself would add one more watch...

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.