这里只以表单里面的“输入密码”和“确认密码”为例,来看一下再angular1.3之前用的$parsers和1.3之后新增的$validators数组实现验证的方法区别。
先来看看HTML代码部分

<!-- 密码型字段 -->
<div>
    <label for="_password">密码</label>
    <input type="password" id="_password" name="password" ng-model="vm.form.password" ng-required="true">
</div>
<div>
    <label for="_retypedPassword">确认密码</label>
    <input type="password" id="_retypedPassword" ng-model="vm.retypedPassword" bf-field-error bf-assert-same-as="vm.form.password">
</div>

里面的bf-field-error指令是用来显示错误消息的,而bf-assert-same-as是特地为“确认密码”新增的指令,用来提示“两次的输入密码不符”的错误提示。
下面是指令bf-field-error的代码

.directive('bfFieldError', function($compile) {
  return {
    restrict: 'A',
    require: 'ngModel',
    link: function(scope, ele, att, ngModelCtrl) {
      //创建一个独立作用于的子scope
      var subScope = scope.$new(true);
      subScope.hasError = function() {
        //除了判断是否无效外,还要判断是否已经输入过
        return ngModelCtrl.$invalid && ngModelCtrl.$dirty;
      };
      subScope.errors = function() {
        //先直接显示到界面上,回头再改进为用户友好的方式
        return ngModelCtrl.$error;
      };
      //把一段HTML编译成"活dom",然后把subScope传递给它,这个"活dom"将会跟随subScope的变化自动更新自己
      var hint = $compile('<ul class="bf-field-error" ng-if="hasError()"><li ng-repeat="(name, wrong) in errors()" ng-if="wrong">{{name | errors}}</li></ul>')(subScope);
      //把这段"活dom"追加到当前元素后面,好让它显示出来
      ele.after(hint);
    }
  };
})

接着是我们的主角bf-assert-same-as

.directive('bfAssertSameAs', function() {
  return {
    restrict: 'A',
    require: 'ngModel',
    link: function(scope, ele, att, ngModelCtrl) {
      var isSame = function(value) {
        //取对照值,通过scope.$eval把att.bfAssertSameAs作为一个表达式在当前作用域中求值,否则它只是一个固定的字符串
        var anotherValue = scope.$eval(att.bfAssertSameAs);
        return value === anotherValue;
      };

      //1.2.x只能用$parsers实现验证
      ngModelCtrl.$parsers.push(function(value) {
        //调用$setValidity设置验证结果,第一个参数是名字,和$error中的属性名一致,但是取值相反,因为这里表示的是“有效”,而$error中表示“无效”
        ngModelCtrl.$setValidity('same', isSame(value));
        return true;
      });
      // 这是assertSameAs验证器所特有的,因为当对照值(即vm.form.password)发生变化时,也要更新有效性状态
      scope.$watch(function() {
        return scope.$eval(att.bfAssertSameAs);
      }, function() {
        //变化时重新判断并设置验证结果
        ngModelCtrl.$setValidity('same', isSame(ele.val()));
      });
    }
  };
})

需要注意的是$setValidity的返回值为 undefined或者false时会在 $error里面会存在parse:true的问题,上面选择了直接返回true
如果用最新的$validators数组则应该这样写,他需要返回布尔类型的值

.directive('bfAssertSameAs', function() {
  return {
    restrict: 'A',
    require: 'ngModel',
    link: function(scope, ele, att, ngModelCtrl) {
      var isSame = function(value) {
        //取对照值,通过scope.$eval把att.bfAssertSameAs作为一个表达式在当前作用域中求值,否则它只是一个固定的字符串
        var anotherValue = scope.$eval(att.bfAssertSameAs);
        return value === anotherValue;
      };
      //1.3.x增加了专门的$validators数组,可用更好的方式实现验证。这里个数组都是放需要被执行的函数的
      ngModelCtrl.$validators.same = function(value) { //返回值必须是boolean类型
        //调用$setValidity设置验证结果,第一个参数是名字,和$error中的属性名一致,但是取值相反,因为这里表示的是“有效”,而$error中表示“无效”
        return isSame(value);
      };
      // 这是assertSameAs验证器所特有的,因为当对照值(即vm.form.password)发生变化时,也要更新有效性状态
      scope.$watch(function() {
        return scope.$eval(att.bfAssertSameAs);
      }, function(newValue) {

        //变化时重新判断并设置验证结果
        ngModelCtrl.$setValidity('same', isSame(ele.val()));
      });
    }
  };
})

效果如下:baidushurufa_2016-10-21_18-25-20


发表评论