之前说过了angular是如何给表单的数据进行基本的,常用的验证的:angular学习笔记(二十)-表单验证

但是在实际工作中,这些验证是远远不够的,很多时候我们需要自定义一些验证规则,以及一些异步,需要向后台发送请求的验证.

这篇文章就来讲解,如何自定义验证规则.

同时,这篇文章还是angular指令中使用ngModelController中关于 $validators 属性和 $asyncValidators 和 $pending

首先,需要自定义指令,设置require属性为'?^ngModel',在指令的link函数中通过第四个参数ctrl,获取到ngModelController这个东西.

ngModelController实例有两个对象,一个是 $validators对象,一个是 $asyncValidators对象.当我们需要添加自定义的同步验证规则时,使用$validators对象,当我们需要添加自定义的异步验证规则时,使用$asyncValidators对象.

通过栗子来说明:

html:

<!DOCTYPE html>
<html ng-app="customControl">
<head>
  <title>asyncValidators</title>
  <meta charset="utf-8">
  <script src="angular-1.3.2.js"></script>
  <script src="script.js"></script>
  <link type="text/css" href="bootstrap.css" rel="stylesheet" />
  <style>
    *{font-family: 'MICROSOFT YAHEI'}
  </style>
</head>
<body>
<div class="container" ng-controller="ctrl">
  <div class="page-header">
    <h1>ngModelController- <small>asyncValidators实现异步验证表单</small></h1>
  </div>
  <form role="form" name="myForm">
    <div class="form-group">
      <input validate-name type="text" name="myWidget" ng-model="userContent" ng-model-options="{updateOn:'blur'}" class="form-control" required>
    </div>
    <div class="alert alert-danger" role="alert" ng-show="myForm.myWidget.$error.required">
      <strong>Oh!</strong> 必填!
    </div>
    <div class="alert alert-danger" role="alert" ng-show="myForm.myWidget.$error.validCharacters">
      <strong>Oh!</strong> 不符合自定义的验证规则!
    </div>
    <div class="alert alert-danger" role="alert" ng-show="myForm.myWidget.$error.uniqueUsername">
      <strong>Oh!</strong> 已经存在的用户名!
    </div>
  </form>
  <div class="panel panel-primary">
    <div class="panel-heading">
      <h3 class="panel-title">用户名:</h3>
    </div>
    <div class="panel-body">
      {{userContent}}
    </div>
  </div>
  <div class="panel panel-primary">
    <div class="panel-heading">
      <h3 class="panel-title">正在异步验证中:</h3>
    </div>
    <div class="panel-body">
      {{myForm.myWidget.$pending}}
    </div>
  </div>
</div>

</body>
</html>

这段html里,input元素使用了validate-name这个指令,后面在js中我们会通过这个指令来给它添加验证.注意元素必须要有ng-model属性,否则也没有什么意义了...

这个指令我们共验证三项,且在元素失去焦点的时候进行验证:

1.required  : ng内置的的验证,需要在指令的最后添加required属性. 然后自己指令里什么也不用写,就可以验证它是否为空了.

2.validCharacters: 自定义的一个同步验证规则,验证输入的内容是否包含'bunny'字符串,自定义的验证不需要在指令最后添加validCharacters属性

3.uniqueUsername: 自定义的一个异步验证规则,验证输入的用户名是否已经被注册,同样,自定义的规则不需要在指令的最后添加uniqueUsername属性

然后来看js代码:

var app = angular.module('customControl',[]);
app.controller('ctrl',function($scope){
});

app.directive('validateName',function($http,$q){
    return {
        restrict:'A',
        require:'?^ngModel',
        link:function(scope,iele,iattr,ctrl){
            ctrl.$validators.validCharacters = function(modelValue, viewValue) {
                var value = modelValue || viewValue;
                return value ? value.indexOf('bunny')!==-1 : true
            };
            ctrl.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
                var value = modelValue || viewValue;
                // Lookup user by username
                return $http.get('/api/users/' + value).
                then(function resolved(res) {
                    if(res.data){
                        //用户名已经存在,验证失败,给下一个promise传递失败通知.
                        return $q.reject('res.data');
                    }
                    else {
                        //用户名不存在,验证成功.
                        return true
                    }

                }, function rejected() {

                })
            };
        }
    }
});

先说 validCharacters 验证: 我们给ctrl(也就是ngModelController的实例)的$validators属性添加了validCharacters属性,它的属性值为一个函数,函数接受两个参数modelValue和viewValue,这两个参数具体分别代表什么,请入angular指令中使用ngModelController查看,总之,这里可以认为就是input的value值.然后我们通过这个自定义的函数的返回值来确定是否通过验证,如果是true,则通过验证,如果是false,则不通过验证,在不通过验证的时候,$error.validCharacters就会为true.

同理,来看uniqueUsername 验证:我们给ctrl的$asyncValidators属性添加了uniqueUsername属性,它的属性值为一个函数,函数接受的参数也同上,然后我们通过$http发送请求来验证用户名是否存在:

node代码:

var express = require('express');
var app = express();

app.use(express.static(__dirname+''));
app.use(express.bodyParser());
app.use(express.methodOverride());

var names = [
    'code_bunny','mi_bunny','hua_bunny'
];

app.get('/api/users/:name',function(req,res){
    setTimeout(function(){
        var name = req.params.name;
        names.forEach(function(list){
            if(name==list) {
                res.send(list);
            }
            else {
                res.end()
            }
        });
    },1000);
});

app.listen(9000);

我们故意延迟了1000毫秒再给出响应,这样,在等待响应的时间里,我们可以看到myForm.myWidget.$pending发生的变化:

表单 指定数组名称和索引值_html

当得到响应后,它就会变为空.

所以,$pending属性里放置的是所以正在等待响应的异步验证.

最重要的一点,$asyncValidators的验证函数返回值比如是一个promise对象.根据这个promise对象发出的通知来决定验证是成功还是失败.如果发出的是失败通知,那么它就是验证失败,比如这里的$q.reject().如果发出的是成功通过,那么他就是验证成功,比如这里的return false.

这里要注意promise的用法:由于$http.get()返回的promise,无论执行的是resolved函数还是rejected函数,他发送给下一个promise的通知总是成功的,所以为了发送失败通知,必须使用$q.reject().

另外,我本来想尝试如果能搜索到用户名,则返回搜索到的用户名,如果不能,则不返回,让它接收到404状态,然后调用rejected函数,但是没能成功,因为如果不返回,它会请求很长一段时间才算请求失败,而不是一旦发现找不到就立刻算请求失败了.所以这里还是根据搜索结果返回不同值来进行判断.