之前說過了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發生的變化:
當得到響應后,它就會變為空.
所以,$pending屬性里放置的是所以正在等待響應的異步驗證.
最重要的一點,$asyncValidators的驗證函數返回值比如是一個promise對象.根據這個promise對象發出的通知來決定驗證是成功還是失敗.如果發出的是失敗通知,那么它就是驗證失敗,比如這里的$q.reject().如果發出的是成功通過,那么他就是驗證成功,比如這里的return false.
這里要注意promise的用法:由於$http.get()返回的promise,無論執行的是resolved函數還是rejected函數,他發送給下一個promise的通知總是成功的,所以為了發送失敗通知,必須使用$q.reject().
另外,我本來想嘗試如果能搜索到用戶名,則返回搜索到的用戶名,如果不能,則不返回,讓它接收到404狀態,然后調用rejected函數,但是沒能成功,因為如果不返回,它會請求很長一段時間才算請求失敗,而不是一旦發現找不到就立刻算請求失敗了.所以這里還是根據搜索結果返回不同值來進行判斷.
完整代碼: https://github.com/OOP-Code-Bunny/angular/tree/master/ngModelController/asyncValidators