AngularJS 1.2.x 學習筆記(表單校驗篇)

在新的項目中巧妙地接觸到了Google提供的前端MVC框架 AngularJS,它的數據雙向綁定真的可以讓前端開發者從繁複的DOM操做中解放出來。css

在我斷斷續續的學習AngularJS一個多月後,能夠來說講個人 AngularJS 學習筆記了,當前首先要講的是前端必不可少的元素——表單,如下用ng代替AngularJS,本文章適合有必定ng基礎的同窗。html

Demo頁面我使用Bootstrap3.2.0做爲前端顯示框架,同時引入了jQuery1.11.1,AngularJS 爲剛剛新發布的1.2.25(使用1.3.0-rc2出現了一個奇怪的BUG,看來仍是穩定版穩定),首先來一個大而全的表單頁面:前端

由於整篇文章的字數限制,移到另外一篇博客了:
http://my.oschina.net/cokolin/blog/526910

請忽略這個表單字段組合的合理性^_^。表單中,爲全部字段綁定了ngModel,設置了校驗指令,例如required,ngMinlength,ngMaxlength等,同時利用了ngShow、ngClass這兩個指令來實現校驗錯誤信息的提示。git

在ngApp內定義一個form,實際上表示這個form已經被ng接管了,在相關的 ngController 的 $scope 域中會自動生成與這個form驗證有關的字段,可是須要注意:json

  1. 給 form 設置 name 屬性才能使用ng的表單校驗,可能設置了name,ng纔會接管這個form瀏覽器

  2. 給 form 設置 novalidate 屬性是關閉瀏覽器內置的校驗器,各個瀏覽器內置的校驗器功能不一,也不夠ng豐富,因此仍是關了比較好;app

  3. 避免給 form 設置 action 屬性,設置了可能會致使表單不經校驗直接提交的結果,固然也能夠用其餘方案解決這個問題;框架

  4. 給 form 設置 autocomplete="off" 是關閉瀏覽器的自動填寫功能,這個功能有時候連按鈕的disabled狀態也記住了,可能會致使避免表單重複提交策略上的問題。
    學習

頁面或CSS中能夠經過 formName.inputFieldName.property 來訪問表單字段的驗證狀態,主要有這幾種狀態:ui

  1. formName.inputFieldName.$pristine,Boolean類型,表單字段是否未修改;

  2. formName.inputFieldName.$dirty,Boolean類型,表單字段是否已修改;

  3. formName.inputFieldName.$valid,Boolean類型,表單字段是否驗證經過;

  4. formName.inputFieldName.$invalid,Boolean類型,表單字段是否未經過驗證;

  5. formName.inputFieldName.$error,Object類型,存儲表單字段驗證項的經過與否,例如
    formName.inputFieldName.$error.required,這個Boolean類型表示表單字段是否未經過必填的驗證;

以上是默認的表單字段的驗證狀態,存儲在$scope域中,表單自身也有這幾個狀態:

  1. formName.$pristine

  2. formName.$dirty

  3. formName.$valid

  4. formName.$invalid

ng會給表單元素加上一些校驗相關的css,表單字段會根據驗證狀態添加這幾個class:

  • .ng-valid

  • .ng-invalid

  • .ng-pristine

  • .ng-dirty

能夠設置這些css的屬性改變表單域的樣式,例如設置:input.ng-invalid {border-color:red;},把未驗證經過的表單元素邊框設置爲紅色。

ng支持大部分的的HTML5表單類型,具體請查看ng的文檔。表單中須要注意的幾點:

  1. 若是在input綁定了ng-model,點擊表單上的reset按鈕並不會重置ng-model中綁定的數據,因此建議給reset按鈕添加ng-click事件實現表單的重置;

  2. select 元素若是綁定了ng-model且無空值的option,那麼ng會自動給select添加一個空option,在用戶選擇select的值之後,這個空 option會自動消失,因此爲了顯示方便,建議給相關的select添加一個value屬性爲空的option,例 如<optionvalue=""></option>,或者使用ng-init給綁定的ng-model初始化一個值;

  3. checkbox 或radio在用戶點擊後會優先執行瀏覽器給定的狀態,因此可能會出現設置ng-checked失效的狀況,例如給checkbox設置ng- checked="selectCount ==10",若是 $scope.selectCount 永遠不等於10,但用戶手動去點擊這個checkbox,依然可使這個checkbox處於選中狀態,不過若是用戶操做其餘元素使 $scope.selectCount 的值改變仍是會致使checkbox的狀態變化的,我當前暫時未發現更優雅的解決方案,主要是給該checkbox添加ng-click事件,重複了 ng-checked 的檢查代碼,而後經過DOM操做從新設置了checkbox的狀態;

表單字段的驗證也能夠自定義,在Demo的表單中,我自定義了一個重複密碼校驗的指令(my-pwd-match="myForm.password"):

myApp.directive('myPwdMatch', [function(){  
    return {  
        restrict: "A",  
        require: 'ngModel',  
        link: function(scope,element,attrs,ctrl){  
            var tageCtrl = scope.$eval(attrs.myPwdMatch);  
            tageCtrl.$parsers.push(function(viewValue){  
                ctrl.$setValidity('pwdmatch', viewValue == ctrl.$viewValue);  
                return viewValue;  
            });  
            ctrl.$parsers.push(function(viewValue){  
                if(viewValue == tageCtrl.$viewValue){  
                    ctrl.$setValidity('pwdmatch', true);  
                    return viewValue;  
                } else{  
                    ctrl.$setValidity('pwdmatch', false);  
                    return undefined;  
                }  
            });  
        }  
    };  
}]);

該自定義指令具體做用是:同時監聽「用戶密碼」和」重複密碼「兩個字段的變化,根據用戶的輸入值的異同設置」重複密碼「驗證狀態:

myForm.rpassword.$error.pwdmatch;

注意:重複密碼輸入框必須綁定ngModel才能使用這個指令。在ng的1.3.0-rc2版本上我定義這個指令在使用時發現ng無端給「重複密碼」設置了一個驗證狀態:

myForm.rpassword.$error.parse;

暫不清楚這個是 BUG 仍是1.2和1.3兩個版本之間指令實現的差別。

上面這個是實時的本地驗證,若是須要要實現字段的遠端驗證,通常驗證的頻率不會那麼頻繁,建議添加一些觸發條件:

  • 當字段值長度等於多少時執行遠端驗證(這個比較適合圖片驗證碼);

  • 延遲驗證,就是等一兩秒數據若是沒有變化再執行驗證;

  • 輸入框丟失焦點後再觸發驗證(適合大部分輸入框);

  • 用戶點擊提交按鈕後再觸發驗證。

開發者可使用這些遠端驗證中的一種或幾種的組合,在肯定輸入框是否處於丟失焦點狀態可使用以下的指令:

myApp.directive('myFocusValid', [function(){  
    return {  
        restrict: "A",  
        require: 'ngModel',  
        link: function(scope,element,attrs,ctrl){  
            ctrl.$focused = false;  
            ctrl.$blured = true;  
            element.bind("focus", function(evt){  
                scope.$apply(function(){  
                    ctrl.$focused = true;  
                    ctrl.$blured = false;  
                });  
            }).bind("blur", function(evt){  
                scope.$apply(function(){  
                    ctrl.$focused = false;  
                    ctrl.$blured = true;  
                });  
            });  
        }  
    };  
}]);


該指令是給輸入框校驗器添加兩個狀態:$focused 和 $blured,爲input添加 my-focus-valid 屬性便可以使用這個指令。

不過我仍是喜歡在用戶點擊按鈕之後再使用遠端驗證,由於這樣作能夠避免在js中寫複雜的阻止表單提交的邏輯,這個表單我使用ngSubmit指令,詳細的實現請看controller:

myApp.controller("myCtrl", ["$scope", "$http", function($scope,$http){  
    console.log($scope)  
      
    $scope.safeTypes = [{  
        value: 0,  
        text: "不保存帳戶狀態"  
    }, {  
        value: 30,  
        text: "保存半個小時"  
    }, {  
        value: 60,  
        text: "保存一個小時"  
    }, {  
        value: 180,  
        text: "保存三個小時"  
    }, {  
        value: 60 * 24,  
        text: "保存一天"  
    }, {  
        value: 60 * 24 * 7,  
        text: "保存一週"  
    }, {  
        value: 60 * 24 * 30,  
        text: "保存一個月"  
    }];  
  
    $scope.$watch("formArgs.username", function(newVal,oldVal){  
        var ctrl = $scope.myForm.username;  
        var usedNames = ctrl.$usedNames;  
        if(usedNames && usedNames[newVal]){  
            ctrl.$setValidity('remoted', false);  
        } else{  
            ctrl.$setValidity('remoted', true);  
        }  
    });  
  
    $scope.doSubmit = function(){  
        var username = $scope.formArgs ? $scope.formArgs.username : undefined;  
        var ctrl = $scope.myForm.username;  
        if(username){  
            $http({  
                method: 'POST',  
                url: 'json/check-username.json',  
                data: {  
                    username: username  
                }  
            }).success(function(resp){  
                if(resp.status != "success"){  
                    ctrl.$setValidity('remoted', false);  
                    if(ctrl.$usedNames){  
                        ctrl.$usedNames[username] = true;  
                    } else{  
                        var obj = {};  
                        obj[username] = true;  
                        ctrl.$usedNames = obj;  
                    }  
                } else{  
                    ctrl.$setValidity('remoted', true);  
                }  
                if($scope.myForm.$valid){  
                    alert("提交表單數據");  
                }  
            }).error(function(){  
                ctrl.$setValidity('remoted', false);  
            });  
        } else{  
            ctrl.$setValidity('remoted', true);  
        }  
        $scope.myForm.submitted = true;  
    }  
}]);


doSubmit事件中,我先判斷用戶名是否能夠已被使用,而後再真正的執行表單的提交動做,同時也不管驗證狀態如何,都會設置表單的 submitted 狀態爲 true,讓校驗的結果可以在頁面上快速的顯示出來。

ng的1.2版本支持的表單元素主要是input、select、button、textarea, 其中input的type主要包括:text、hidden、password、checkbox、radio、file、image、reset、 submit和擴展的email、url、number,1.3版在1.2版的基礎上又擴展了一些字段,包括:

  1. dateTimeLocal:yyyy-MM-dd'T'HH:mm:ss;

  2. date:yyyy-MM-dd;

  3. month:yyyy-MM;

  4. time:HH:mm:ss;

  5. week:yyyy-W##;

這些日期類型,全部的日期字段默認都是ISO的標準樣式,但跟國內的習慣仍是有一些差別,例如 dateTimeLocal中間多了一個字母T,week表示該年的第幾周,例如2014-W49,這些日期類型的加入大大的加強了ng的功能,但願 1.3版本可以儘快的實現stable,不過不知道ng是否支持<input type="range">這個值域類型呢?

表單驗證的效果圖:

項目工程文件:

http://download.csdn.net/download/cackling/7954867

之後有時間會提供一下 git 項目地址。

做者:cackling

相關文章
相關標籤/搜索