angular中的表單資料自定義驗證

詩&遠方發表於2014-11-24

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

 

相關文章