Angularjs $ setValidity通过不起作用的服务

时间:2015-08-21 14:13:14

标签: angularjs validation

我正在angularjs中构建一个验证模块,以改进一些开箱即用的基本html5验证。我的工作基于jquery验证插件。我遇到的问题是,即使一切似乎都正常工作,表单也无法正确验证。具体来说,我目前正在测试一个比标准html5版本更不慷慨的电子邮件验证,因为它需要一个包含顶级域名的完整电子邮件,但是当我调用$setValidity('email',false);时,表单仍然有效。我的代码在

之下

验证服务

'use strict';

/* Services */

function validations() {
  /**
   * Created by rich on 8/14/15.
   *
   * Most of these regex statements were kifed from jquery validation plugin with perhaps a little bit of sugar
   * added to some by yours truly.
   *
   * Thanks to jzaeffer for his efforts.
   * http://jqueryvalidation.org
   * https://github.com/jzaefferer/jquery-validation
   */


  var tests = {
    //validates full email address with a valid domain. [string]@[string].[string]
    email: function(value, param, elem) {
      return /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])+\.+(?:[a-zA-Z0-9\.](?:[a-zA-Z0-9-\.]{0,61}[a-zA-Z0-9])?)$/.test(value);
    },
    url: function(value, param, elem) {
      return /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(value)
    },
    integer: function(value, param, elem) {
      return /^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(value);
    },
    creditcard: function(value, param, elem) {
      // accept only spaces, digits and dashes
      if (/[^0-9 \-]+/.test(value)) {
        return false;
      }
      var nCheck = 0,
        nDigit = 0,
        bEven = false,
        n, cDigit;
      value = value.replace(/\D/g, "");
      // Basing min and max length on
      // http://developer.ean.com/general_info/Valid_Credit_Card_Types
      if (value.length < 13 || value.length > 19) {
        return false;
      }
      for (n = value.length - 1; n >= 0; n--) {
        cDigit = value.charAt(n);
        nDigit = parseInt(cDigit, 10);
        if (bEven) {
          if ((nDigit *= 2) > 9) {
            nDigit -= 9;
          }
        }
        nCheck += nDigit;
        bEven = !bEven;
      }
      return (nCheck % 10) === 0;
    },
    min: function(value, param, elem) {
      return value >= param
    },
    max: function(value, param, elem) {
      return value <= param
    },
    between: function(value, param, elem) {
      return (value >= param[0] && value <= param[1]);
    },
    equalTo: function(value, param, elem) {
      return value === elem.val();
    },
    //Accept specific mime type
    accept: function(value, param, elem) {
      // Split mime on commas in case we have multiple types we can accept
      var typeParam = typeof param === "string" ? param.replace(/\s/g, "").replace(/,/g, "|") : "image/*",
        i, file;
      // If we are using a wildcard, make it regex friendly
      typeParam = typeParam.replace(/\*/g, ".*");
      // Check if the element has a FileList before checking each file
      if (elem.files && elem.files.length) {
        for (i = 0; i < elem.files.length; i++) {
          file = elem.files[i];
          // Grab the mimetype from the loaded file, verify it matches
          if (!file.type.match(new RegExp("\\.?(" + typeParam + ")$", "i"))) {
            return false;
          }
        }
      }
      // Either return true because we've validated each file, or because the
      // browser does not support element.files and the FileList feature
      return true;
    },
    alphanumeric: function(value, param, elem) {
      /^\w+$/i.test(value);
    },
    ipv4: function(value, param, elem) {
      return /^(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)$/i.test(value);
    },
    ipv6: function(value, param, elem) {
      return /^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i.test(value)
    },
    phone: function(value, param, elem) {
      return (value.length > 9 && value.match(/^(\+?1-?)?(\([2-9]([02-9]\d|1[02-9])\)|[2-9]([02-9]\d|1[02-9]))-?[2-9]([02-9]\d|1[02-9])-?\d{4}$/));
    },
    zip: function(value, param, elem) {
      return /^\d{5}(-\d{4})?$/.test(value)
    }
  };

  function addTest(name, test) {
    tests[name] = test;
  }

  function isValid(test, value, params, elem) {
    var valid = tests[test](value, params, elem);
    return valid
  }

  return {
    addTest: function(name, test) {
      addTest(name, test);
    },
    test: function(testName, options) {
      if (typeof options === 'object') {
        return isValid(testName, options.value, options.param, options.elem);
      } else {
        //options is actually just the value to be tested
        return isValid(testName, options);
      }
    },
    validate: function(name, ngModel) {
      var validator;
      var self = this;
      if (self.isType(name)) {
        validator = function(ngModelValue) {
          //Custom Property used for debugging
          ngModel.$setValiditityTo = this.test(name, ngModelValue);
          //Set validity based on results of test
          ngModel.$setValidity(name, this.test(name, ngModelValue));
          return ngModelValue
        };
        //Bind validator to current this object so that it can run the required tests.
        ngModel.$parsers.push(validator.bind(self));
      }
    },
    isType: function(name) {
      var types = ['email', 'url', 'creditcard', 'integer', 'alphanumeric', 'ipv4', 'ipv6', 'phone', 'zip'];
      return types.indexOf(name) > -1;
    }
  }
}

app.factory('validations', validations);

输入指令

function input(validations) {
  return {
    restrict: 'E',
    require: 'ngModel',
    scope: {
      type: '@'
    },
    link: function(scope, elem, attr, ctrl) {
      validations.validate(scope.type, ctrl);
    }
  };
}
app.directive('input', input);

在下面的plunker中,您可以看到自定义$setValidityTo对象设置正确,但errors对象遵循电子邮件验证的正常行为。

http://plnkr.co/edit/68ItZrGSlN9Yf080ctBe?p=preview

1 个答案:

答案 0 :(得分:1)

问题是当字段无效时验证函数返回的内容:

PreviewCallback

以上总是返回模型值。

回想一下,$ parsers / $ formatters是一个“管道”。验证函数按添加顺序执行,如果其中一个验证函数认为该值无效,则返回undefined。

当验证函数返回undefined时,管道中剩余的验证器函数不会被执行。

Working plunkr,以及更改过的代码:

validator = function(ngModelValue) {
  //Custom Property used for debugging
  ngModel.$setValiditityTo = this.test(name, ngModelValue);
  //Set validity based on results of test
  ngModel.$setValidity(name, this.test(name, ngModelValue));
  return ngModelValue
}