Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the purpose of the new built in 'parse' validation key in Angular 1.3?

The behavior of the ngModelController parsing pipeline seems to have changed between Angular 1.2 and 1.3. I now always see a new validation key named 'parse' added to all $error objects, and whenever one of the parsers returns undefined, it overrides/replaces all other validation keys that may have been already set.

For instance, here is a working example in Angular 1.2.23 - try entering a number out of range:

http://jsfiddle.net/8doq0saf/5/

The same thing running under 1.3-rc gives a different result:

http://jsfiddle.net/1t52s9b2/4/

I have not yet been able to find any documentation on this change. What is the purpose of the parse key, and how do I change my code to get back the old behavior?

angular.module('app', []).directive('number', function () {
    return {
        require: 'ngModel',
        link: function (scope, elem, attrs, ctrl) {

            // valid number
            ctrl.$parsers.push(function (value) {
                var valid = angular.isUndefined(value) || value === '' || isFinite(value);
                ctrl.$setValidity('number', valid);
                return valid
                    ? angular.isUndefined(value) || value === '' ? undefined : Number(value)
                    : undefined;
            });

            ctrl.$parsers.push(function (value) {
                if (!angular.isDefined(attrs.minNumber)) {
                    return value;
                }
                var valid = angular.isUndefined(value) || Number(value) >= Number(attrs.minNumber);
                ctrl.$setValidity('minNumber', valid);
                return valid ? value : undefined;
            });

            ctrl.$parsers.push(function (value) {
                if (!angular.isDefined(attrs.maxNumber)) {
                    return value;
                }
                var valid = angular.isUndefined(value) || Number(value) <= Number(attrs.maxNumber);
                ctrl.$setValidity('maxNumber', valid);
                return valid ? value : undefined;
            });
        }
    };
});
like image 266
Mike Chamberlain Avatar asked Sep 09 '14 08:09

Mike Chamberlain


People also ask

Why do we need form validation in Angular 2?

So in order to validate the user data at the client level only we require form validation. In angular we have two different approaches that is template driven and reactive forms both of them have their own advantage and disadvantage and specific purpose when to use which one.

How to disable default browser validation in AngularJS?

The HTML form attribute novalidate is used to disable default browser validation. The AngularJS directive ng-model binds the input elements to the model. The model object has two properties: user and email.

What are async validators in angular?

Async validators: Asynchronous functions that take a control instance and return a Promise or Observable that later emits a set of validation errors or null. You can pass these in as the third argument when you instantiate a FormControl. For performance reasons, Angular only runs async validators if all sync validators pass.

What are built-in directives in angular?

Built-in directives link Directives are classes that add additional behavior to elements in your Angular applications. With Angular's built-in directives, you can manage forms, lists, styles, and what users see. See the live example / download example for a working example containing the code snippets in this guide.


1 Answers

Angular 1.3 rationalized things to make a clean distinction between parsing and validating.

Parsing

Angular now automatically adds a 'parse' key to all $error collections with its value set accordingly - true if any of the parsers returned undefined, false otherwise.

For an unparsable value (alphas entered for a number, badly formatted date, etc), we should return undefined from the parser. This will cause Angular to remove any $error keys already set, and replace the entire object with just { "parse": true }. No more parsers will be run. The model will not be updated. The $parsers array should now only be used for parsing.

Validation

ngModelController has a new $validators property, to which we can assign validation functions. These will only be run if the parsing pipeline was successful. Return false from one of these functions for a value that is parsable as the required datatype, but merely invalid (string too long, number out of range etc). The name of the validator function becomes the validation key in the $error object. All validators will be run, even if one returns false. The model will only be updated if validation is successful.

This is potentially a breaking change for existing applciations, as people frequently returned undefined from parsers for an invalid value. Here is what I had, which is a typical example:

ctrl.$parsers.push(function (value) {
    if (!angular.isDefined(attrs.minNumber)) {
        return value;
    }
    var valid = angular.isUndefined(value) || Number(value) >= Number(attrs.minNumber);
    ctrl.$setValidity('minNumber', valid);
    return valid ? value : undefined;
});

Under this new scheme, this should be moved to a validation function instead:

ctrl.$validators.minNumber = function (value) {
    return !value || !angular.isDefined(attrs.minNumber) || (value >= Number(attrs.minNumber));
});

Here is the directive with everything fixed up:

angular.module('app', []).directive('number', function () {
    return {
        require: 'ngModel',
        link: function (scope, elem, attrs, ctrl) {

            // valid number
            ctrl.$parsers.push(function (value) {
                if(value === '') return value;
                return isFinite(value) ? Number(value) : undefined;
            });

            ctrl.$validators.minNumber = function (value) {
                return !value || !angular.isDefined(attrs.minNumber) || (value >= Number(attrs.minNumber));
            };

            ctrl.$validators.maxNumber = function (value) {
                return !value || !angular.isDefined(attrs.maxNumber) || (value <= Number(attrs.maxNumber));
            };
        }
    };
});

http://jsfiddle.net/snkesLv4/10/

I really like this new way - it is much cleaner.

like image 126
Mike Chamberlain Avatar answered Oct 25 '22 12:10

Mike Chamberlain