自定义表单验证

  <input type="text" ng-model='li' fv>


/*
  要使用link的第四个参数有两个必要条件
    1 require存在
    2 必须以属性或类名的方式存在于一个input标签中,并且该标签有ng-model属性;

 */
  app.directive('fv', function(){
    return {
      restrict: 'EA',
      require: 'ngModel',
      link: function(scope, ele, attr, ngmodel){
        console.info('lifei',ngmodel);
      }
    }
  })

一下是输出的ngModel里面的内容
  1. NgModelController
    1. $asyncValidators:Object     异步验证
    2. $commitViewValue:()
    3. $dirty:false                                  是否修改
    4. $error:Object                                错误
    5. $formatters:Array[1]                      数据格式化的管道函数数组
    6. $invalid:false                                是否不合法
    7. $isEmpty:(value)                               是否是空
    8. $modelValue:undefined                     当前的valud值
    9. $name:""                                                                               input的name
    10. $options:null 
    11. $parsers:Array[0]                                                                 设置表单合法性的管道函数数组
    12. $pending:undefined
    13. $pristine:true
    14. $render:()
    15. $rollbackViewValue:()
    16. $setDirty:()                                                                          设置是否已修改
    17. $setPristine:()
    18. $setTouched:()
    19. $setUntouched:()
    20. $setValidity:setValidity(validationErrorKey, state, controller)         设置表单的某一验证的合法性
    21. $setViewValue:(value, trigger)                         设置valud值
    22. $touched:false
    23. $untouched:true
    24. $valid:true                                                验证时候合法
    25. $validate:()
    26. $validators:Object                                  同步验证
    27. $viewChangeListeners:Array[0]               当value值改变时,执行的管道函数数组
    28. $viewValue:undefined               当前input的ng-model的值
    29. __proto__:Object

<form name='myForm'>
  <input type="text" name='li1' ng-model="lifei1">
  <input type="text" name="li2" pwd-match='myForm.li1' ng-model='lifei2'>
</form>


  
//判断两次输入的密码是否相同
app.directive('myPwdMatch', [function(){ return {

//如果用link的第四个参数ctrl,必须有require:'ngModel',和类型必须是属性 restrict:
"A", require: 'ngModel', link: function(scope,element,attrs,ctrl){

//attrs.myPwdMatch的属性值是formName.input1Name字符串,用scope.$eval()来解析字符串,获取对应的input1Name对象
var tageCtrl = scope.$eval(attrs.myPwdMatch);

//添加自定义表单验证的合法性 tageCtrl.$parsers.push(
function(viewValue){

//设置当前input的pwdmatch的合法性,如果input1的value值 等于当前input的value值,认为他合法 ctrl.$setValidity(
'pwdmatch', viewValue == ctrl.$viewValue); return viewValue; });

//当前input添加表单验证的合法性 ctrl.$parsers.push(
function(viewValue){ if(viewValue == tageCtrl.$viewValue){ ctrl.$setValidity('pwdmatch', true); return viewValue; } else{ ctrl.$setValidity('pwdmatch', false); return undefined; } }); } }; }]);

也就是说: $validators和$asyncValidators这两个管道是$parsers管道函数数组里面的其中两个比较特殊的管道;

*5. $validators

一个json对象.

{
   validateName: function(modelValue,viewValue){
       return ...
   }
}

当$setViewValue(value)被赋值给$modelValue之前,会经过$parsers管道,经过$parsers管道时,就会经过这个$validators管道.其中validateName是验证的名字,函数是这个验证的方法,其中的参数modelValue和viewValue就是$modelValue和$viewValue,如果返回值是true,则通过validateName

验证,如果返回值是false,则没有通过validateName验证,如果没有通过validateName验证,$error.validateName就会为true.这就是angular内部验证表单项的原理.

eg: 自定义一个验证规则,输入内容中必须包含数字

<div class="alert alert-danger" role="alert" ng-show="myForm.myWidget.$error.validCharacters">
    <strong>Oh!</strong> 不符合自定义的验证规则!
</div>
ngModel.$validators.validCharacters = function(modelValue, viewValue) {
    var value = modelValue || viewValue;
    return /[0-9]+/.test(value);
};

*6.$asyncValidators

一个json对象.用来处理异步验证(比如一个http请求). 

{
   validateName: function(modelValue,viewValue){
       return promise
   }
}

其中validateName是验证的名字,函数是这个验证的方法,其中的参数modelValue和viewValue就是$modelValue和$viewValue,返回值必须是一个promise对象,如果这个promise对象传递给它下一个.then方法失败通知,则不通过validateName验证,如果这个promise对象传递给它下一个.then方法成功通知,则表示通过validateName验证.当异步验证开始执行的时候,所有的异步验证都是平行并发的.只有当所有的验证都通过时,数据模型才会被同步更新.只要有一个异步验证没有完成,这个验证名就会被放到ngModelController的$pending属性中.另外,所有的异步验证都只会在所有的同步验证通过以后才开始.

核心代码:

<input validate-name type="text" name="myWidget" ng-model="userContent" ng-model-options="{updateOn:'blur'}" class="form-control" required uniqueUsername>

<div class="alert alert-danger" role="alert" ng-show="myForm.myWidget.$error.uniqueUsername">
      <strong>Oh!</strong> 已经存在的用户名!
</div>
复制代码
app.directive('validateName',function($http,$q){
    return {
        restrict:'A',
        require:'?^ngModel',
        link:function(scope,iele,iattr,ctrl){
            ctrl.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
                var value = modelValue || viewValue;
                // 异步验证用户名是否已经存在
                return $http.get('/api/users/' + value).
                then(function resolved(res) {
                    if(res.data){
                        //用户名已经存在,验证失败,给下一个promise传递失败通知.
                        return $q.reject('res.data');
                    }
                    else {
                        //用户名不存在,验证成功.
                        return true
                    }

                }, function rejected() {
                        //请求失败
                })
            };
        }
    }
});
复制代码

一个指令用于异步验证是否重命名:

    /*异步验证是否重命名
    *     例子: <input type="text"   name="name" ng-model="form.name" create-head='{"url":"app_name/used","param":"app_name={value}"}'/>
    
    1 自定义验证: require,属性
        1 restrict必须是属性,requre必须是'ngModel'
    2 进行异步验证设置 $asyncValidators = {验证名: 验证函数}
    2 获取请求的url,用于ajax请求
        1 用先把input标签上的createHead属性的json字符串获取
        2 再用angular.formJson()解析成json对象
        3 再用string.replace(string,value),把占位符"{value}" 替换成input的modelValue值(scope上的值)
        4 用commonService来拼接url
    3 创建一个defered对象,用来返回状态
        1 用var a = $q.defer()创建
        2 a.reject()表示拒绝
        3 a.resolve()表示成功
    4 $http请求
        1 method,url,headers
        2 返回的是一个promise对象
    5 分情况验证
        1 如果成功,说明找到了,命名重复,验证不通过,返回a.reject();
        2 如果404,说明没找到了,命名不重复,验证通过,返回a.resolve();
        3 如果是其他情况,不能确定,是否重命名,按验证不通过算,赶回a.reject();
    6 返回一个promise对象
    * */

app.directive("createHead", ["$q", "$http", "$cookies", "AppConfig", "commonService", function($q, $http, $cookies, AppConfig, commonService){
    return{
        restrict:"A",
        require:"ngModel",
        scope:{
            version:"="
        },
        link:function(scope,ele,attrs,ctl){
            console.info("ctrl",ctl);
            //格式demo:  create-head='{"url":"app_name/used","param":"app_name={value}"}'
            ctl.$asyncValidators.checkasync = function(modelValue,viewValue){
                var d = $q.defer();
                //angular.formJson()方法可以把json字符串解析成json对象;
                var _a = angular.fromJson(attrs.createHead);

                _a.url = _a.url.replace("{value}",modelValue);
                if(_a.param){
                    _a.param = _a.param.replace("{value}",modelValue);
                }

                $http({
                    method:"HEAD",
                    url:commonService.getServerAuthUrl(_a.url,_a.param?_a.param:null),
                    headers:{
                        "Authorization": $cookies.get("token")
                    }
                }).then(function(e){
                    //200 已存在
                    d.reject();
                },function(e){
                    if(e.status==404){
                        //不存在
                        d.resolve();
                    }
                    else{
                        d.reject();
                    }
                });
                return d.promise;
            };
        }
    };
}]);
原文地址:https://www.cnblogs.com/bridge7839/p/6567184.html