之前說過了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