How to add custom validation to an AngularJS form?

后端 未结 12 1509
既然无缘
既然无缘 2020-11-22 10:07

I have a form with input fields and validation setup by adding the required attributes and such. But for some fields I need to do some extra validation. How wou

相关标签:
12条回答
  • 2020-11-22 10:46

    Some great examples and libs presented in this thread, but they didn't quite have what I was looking for. My approach: angular-validity -- a promise based validation lib for asynchronous validation, with optional Bootstrap styling baked-in.

    An angular-validity solution for the OP's use case might look something like this:

    <input  type="text" name="field4" ng-model="field4"
            validity="eval"
            validity-eval="!(field1 && field2 && field3 && !field4)"
            validity-message-eval="This field is required">
    

    Here's a Fiddle, if you want to take it for a spin. The lib is available on GitHub, has detailed documentation, and plenty of live demos.

    0 讨论(0)
  • 2020-11-22 10:47

    You can use Angular-Validator.

    Example: using a function to validate a field

    <input  type = "text"
        name = "firstName"
        ng-model = "person.firstName"
        validator = "myCustomValidationFunction(form.firstName)">
    

    Then in your controller you would have something like

    $scope.myCustomValidationFunction = function(firstName){ 
       if ( firstName === "John") {
           return true;
        }
    

    You can also do something like this:

    <input  type = "text"
            name = "firstName"
            ng-model = "person.firstName"
            validator = "'!(field1 && field2 && field3)'"
            invalid-message = "'This field is required'">
    

    (where field1 field2, and field3 are scope variables. You might also want to check if the fields do not equal the empty string)

    If the field does not pass the validator then the field will be marked as invalid and the user will not be able to submit the form.

    For more use cases and examples see: https://github.com/turinggroup/angular-validator

    Disclaimer: I am the author of Angular-Validator

    0 讨论(0)
  • 2020-11-22 10:51

    I extended @Ben Lesh's answer with an ability to specify whether the validation is case sensitive or not (default)

    use:

    <input type="text" name="fruitName" ng-model="data.fruitName" blacklist="Coconuts,Bananas,Pears" caseSensitive="true" required/>
    

    code:

    angular.module('crm.directives', []).
    directive('blacklist', [
        function () {
            return {
                restrict: 'A',
                require: 'ngModel',
                scope: {
                    'blacklist': '=',
                },
                link: function ($scope, $elem, $attrs, modelCtrl) {
    
                    var check = function (value) {
                        if (!$attrs.casesensitive) {
                            value = (value && value.toUpperCase) ? value.toUpperCase() : value;
    
                            $scope.blacklist = _.map($scope.blacklist, function (item) {
                                return (item.toUpperCase) ? item.toUpperCase() : item
                            })
                        }
    
                        return !_.isArray($scope.blacklist) || $scope.blacklist.indexOf(value) === -1;
                    }
    
                    //For DOM -> model validation
                    modelCtrl.$parsers.unshift(function (value) {
                        var valid = check(value);
                        modelCtrl.$setValidity('blacklist', valid);
    
                        return value;
                    });
                    //For model -> DOM validation
                    modelCtrl.$formatters.unshift(function (value) {
                        modelCtrl.$setValidity('blacklist', check(value));
                        return value;
                    });
                }
            };
        }
    ]);
    
    0 讨论(0)
  • 2020-11-22 10:53

    @synergetic I think @blesh suppose to put function validate as below

    function validate(value) {
        var valid = blacklist.indexOf(value) === -1;
        ngModel.$setValidity('blacklist', valid);
        return valid ? value : undefined;
    }
    
    ngModel.$formatters.unshift(validate);
    ngModel.$parsers.unshift(validate);
    
    0 讨论(0)
  • 2020-11-22 11:00

    Edit: added information about ngMessages (>= 1.3.X) below.

    Standard form validation messages (1.0.X and above)

    Since this is one of the top results if you Google "Angular Form Validation", currently, I want to add another answer to this for anyone coming in from there.

    There's a method in FormController.$setValidity but that doesn't look like a public API so I rather not use it.

    It's "public", no worries. Use it. That's what it's for. If it weren't meant to be used, the Angular devs would have privatized it in a closure.

    To do custom validation, if you don't want to use Angular-UI as the other answer suggested, you can simply roll your own validation directive.

    app.directive('blacklist', function (){ 
       return {
          require: 'ngModel',
          link: function(scope, elem, attr, ngModel) {
              var blacklist = attr.blacklist.split(',');
    
              //For DOM -> model validation
              ngModel.$parsers.unshift(function(value) {
                 var valid = blacklist.indexOf(value) === -1;
                 ngModel.$setValidity('blacklist', valid);
                 return valid ? value : undefined;
              });
    
              //For model -> DOM validation
              ngModel.$formatters.unshift(function(value) {
                 ngModel.$setValidity('blacklist', blacklist.indexOf(value) === -1);
                 return value;
              });
          }
       };
    });
    

    And here's some example usage:

    <form name="myForm" ng-submit="doSomething()">
       <input type="text" name="fruitName" ng-model="data.fruitName" blacklist="coconuts,bananas,pears" required/>
       <span ng-show="myForm.fruitName.$error.blacklist">
          The phrase "{{data.fruitName}}" is blacklisted</span>
       <span ng-show="myForm.fruitName.$error.required">required</span>
       <button type="submit" ng-disabled="myForm.$invalid">Submit</button>
    </form>
    

    Note: in 1.2.X it's probably preferrable to substitute ng-if for ng-show above

    Here is an obligatory plunker link

    Also, I've written a few blog entries about just this subject that goes into a little more detail:

    Angular Form Validation

    Custom Validation Directives

    Edit: using ngMessages in 1.3.X

    You can now use the ngMessages module instead of ngShow to show your error messages. It will actually work with anything, it doesn't have to be an error message, but here's the basics:

    1. Include <script src="angular-messages.js"></script>
    2. Reference ngMessages in your module declaration:

      var app = angular.module('myApp', ['ngMessages']);
      
    3. Add the appropriate markup:

      <form name="personForm">
        <input type="email" name="email" ng-model="person.email" required/>
      
        <div ng-messages="personForm.email.$error">
          <div ng-message="required">required</div>
          <div ng-message="email">invalid email</div>
        </div>
      </form>
      

    In the above markup, ng-message="personForm.email.$error" basically specifies a context for the ng-message child directives. Then ng-message="required" and ng-message="email" specify properties on that context to watch. Most importantly, they also specify an order to check them in. The first one it finds in the list that is "truthy" wins, and it will show that message and none of the others.

    And a plunker for the ngMessages example

    0 讨论(0)
  • 2020-11-22 11:02

    Update:

    Improved and simplified version of previous directive (one instead of two) with same functionality:

    .directive('myTestExpression', ['$parse', function ($parse) {
        return {
            restrict: 'A',
            require: 'ngModel',
            link: function (scope, element, attrs, ctrl) {
                var expr = attrs.myTestExpression;
                var watches = attrs.myTestExpressionWatch;
    
                ctrl.$validators.mytestexpression = function (modelValue, viewValue) {
                    return expr == undefined || (angular.isString(expr) && expr.length < 1) || $parse(expr)(scope, { $model: modelValue, $view: viewValue }) === true;
                };
    
                if (angular.isString(watches)) {
                    angular.forEach(watches.split(",").filter(function (n) { return !!n; }), function (n) {
                        scope.$watch(n, function () {
                            ctrl.$validate();
                        });
                    });
                }
            }
        };
    }])
    

    Example usage:

    <input ng-model="price1" 
           my-test-expression="$model > 0" 
           my-test-expression-watch="price2,someOtherWatchedPrice" />
    <input ng-model="price2" 
           my-test-expression="$model > 10" 
           my-test-expression-watch="price1" 
           required />
    

    Result: Mutually dependent test expressions where validators are executed on change of other's directive model and current model.

    Test expression has local $model variable which you should use to compare it to other variables.

    Previously:

    I've made an attempt to improve @Plantface code by adding extra directive. This extra directive very useful if our expression needs to be executed when changes are made in more than one ngModel variables.

    .directive('ensureExpression', ['$parse', function($parse) {
        return {
            restrict: 'A',
            require: 'ngModel',
            controller: function () { },
            scope: true,
            link: function (scope, element, attrs, ngModelCtrl) {
                scope.validate = function () {
                    var booleanResult = $parse(attrs.ensureExpression)(scope);
                    ngModelCtrl.$setValidity('expression', booleanResult);
                };
    
                scope.$watch(attrs.ngModel, function(value) {
                    scope.validate();
                });
            }
        };
    }])
    
    .directive('ensureWatch', ['$parse', function ($parse) {
        return {
            restrict: 'A',
            require: 'ensureExpression',
            link: function (scope, element, attrs, ctrl) {
                angular.forEach(attrs.ensureWatch.split(",").filter(function (n) { return !!n; }), function (n) {
                    scope.$watch(n, function () {
                        scope.validate();
                    });
                });
            }
        };
    }])
    

    Example how to use it to make cross validated fields:

    <input name="price1"
           ng-model="price1" 
           ensure-expression="price1 > price2" 
           ensure-watch="price2" />
    <input name="price2" 
           ng-model="price2" 
           ensure-expression="price2 > price3" 
           ensure-watch="price3" />
    <input name="price3" 
           ng-model="price3" 
           ensure-expression="price3 > price1 && price3 > price2" 
           ensure-watch="price1,price2" />
    

    ensure-expression is executed to validate model when ng-model or any of ensure-watch variables is changed.

    0 讨论(0)
提交回复
热议问题