欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

由淺入深剖析Angular表單驗證

 更新時間:2016年07月14日 11:45:06   作者:給力叔  
這篇文章主要介紹了由淺入深剖析Angular表單驗證 的相關(guān)資料,需要的朋友可以參考下

最近手上維護的組件剩下的BUG都是表單驗證,而且公司的表單驗證那塊代碼經(jīng)歷的幾代人,里面的邏輯開始變得不清晰,而且代碼結(jié)構(gòu)不是很angular。

是很有必要深入了解表單驗證。

<body ng-controller="MainController">
<form name="form" novalidate="novalidate">
<input name="text" type="email" ng-model="name">
</form>
</body>

ngModel是angular的黑魔法,實現(xiàn)雙向綁定,當name的值變化的時候,input的value也會跟著變化。

當用戶在input修改value的時候,name的值也會跟著變化。

novalidate="novalidate"的目的是去除系統(tǒng)自帶的表單驗證。

上面那段代碼解析完,angular會在MainController的$scope下面生成一個變量"form",$scope.form,這個變量的名稱跟html中form.name一致。

而$scope.form.text為文本輸入框的Model,繼承自ngModelController。

其中$scope.form實例自FormController。其內(nèi)容為:

文本輸入框的Model(也就是$scope.form.text)為:

其中$dirty/$pristine,$valid/$invalid,$error為常用屬性。尤其是$error。

最簡單的表單驗證:

了解了form和輸入框,就可以先擼個最簡單的顯示錯誤的指令。

html內(nèi)容如下:

<form name="form" novalidate="novalidate">
<input name="text" type="email" ng-model="name" error-tip>
</form>

指令代碼如下:

// 當輸入框出錯,就顯示錯誤
directive("errorTip",function($compile){
return {
restrict:"A",
require:"ngModel",
link:function($scope,$element,$attrs,$ngModel){
//創(chuàng)建子scope
var subScope = $scope.$new(),
//錯誤標簽的字符串,有錯誤的時候,顯示錯誤內(nèi)容
tip = '<span ng-if="hasError()">{{errors() | json}}</span>';
//臟,而且無效,當然屬于錯誤了
$scope.hasError = function(){
return $ngModel.$invalid && $ngModel.$dirty;
} //放回ngModel的錯誤內(nèi)容,其實就是一個對象{email:true,xxx:true,xxxx:trie}
$scope.errors = function(){
return $ngModel.$error;
}
//編譯錯誤的指令,放到輸入框后面
$element.after($compile(tip)(subScope));
}
}
});

先看看執(zhí)行結(jié)果:

輸入無效的郵箱地址的時候:

輸入正確的郵箱地址的時候:

errorTip指令一開始通過 require:"ngModel" 獲取ngModelController。然后創(chuàng)建用于顯示錯誤的元素到輸入框。

這里使用了$compile,$compile用于動態(tài)編譯顯示html內(nèi)容的。

當有錯誤內(nèi)容的時候,錯誤的元素就會顯示。

為什么subScope可以訪問hasError和errors方法?

因為原型鏈。看下圖就知道了。

自定義錯誤內(nèi)容

好了,很明顯現(xiàn)在的表單驗證是不能投入使用的,我們必須自定義顯示的錯誤內(nèi)容,而且要顯示的錯誤不僅僅只有一個。

顯示多個錯誤使用ng-repeat即可,也就是把"errorTip"指令中的

tip = '<span ng-if="hasError()">{{errors() | json}}</span>';

改成:

tip = '<ul ng-if="hasError()" ng-repeat="(errorKey,errorValue) in errors()">' +
'<span ng-if="errorValue">{{errorKey | errorFilter}}</span>' +
'</ul>';

其中errorFilter是一個過濾器,用于自定義顯示錯誤信息的。過濾器其實是個函數(shù)。

其代碼如下:

.filter("errorFilter",function(){
return function(input){
var errorMessagesMap = {
email:"請輸入正確的郵箱地址",
xxoo:"少兒不宜"
}
return errorMessagesMap[input];
}
});

結(jié)果如下:

好了,到這里就能夠處理“簡單”的表單驗證了。對,簡單的。我們還必須繼續(xù)深入。

自定義表單驗證!

那我們就來實現(xiàn)一個不能輸入“帥哥”的表單驗證吧。

指令如下:

.directive("doNotInputHandsomeBoy",function($compile){
return {
restrict:"A",
require:"ngModel",
link:function($scope,$element,$attrs,$ngModel){
$ngModel.$parsers.push(function(value){
if(value === "帥哥"){
//設(shè)置handsome為無效,設(shè)置它為無效之后,$error就會變成{handsome:true}
$ngModel.$setValidity("handsome",false);
}
return value;
})
}
}
})

結(jié)果如下:

這里有兩個關(guān)鍵的東西,$ngModel.$parsers和$ngModel.$setValidity.

$ngModel.$parsers是一個數(shù)組,當在輸入框輸入內(nèi)容的時候,都會遍歷并執(zhí)行$parsers里面的函數(shù)。

$ngModel.$setValidity("handsome",false);設(shè)置handsome為無效,會設(shè)置$ngModel.$error["handsome"] = true;

也會設(shè)置delete $ngModel.$$success["handsome"],具體可以翻翻源碼。

這里我總結(jié)一下流程。

-->用戶輸入

-->angular執(zhí)行所有$parsers中的函數(shù)

-->遇到$setValidity("xxoo",false);那么就會把xxoo當做一個key設(shè)置到$ngModel.$error["xxoo"]

-->然后errorTip指令會ng-repeat $ngModel.$error

-->errorFilter會對錯誤信息轉(zhuǎn)義

-->最后顯示錯誤的信息

自定義輸入框的顯示內(nèi)容

很多時候開發(fā),不是簡簡單單驗證錯誤顯示錯誤那么簡單。有些時候我們要格式化輸入框的內(nèi)容。

例如,"1000"顯示成"1,000"

"hello"顯示成"Hello"

現(xiàn)在讓我們實現(xiàn)自動首字母大寫。

源碼如下:

<form name="form" novalidate="novalidate">
<input name="text" type="text" ng-model="name" upper-case>
</form> 
.directive("upperCase",function(){
return {
restrict:"A",
require:"ngModel",
link:function($scope,$element,$attrs,$ngModel){
$ngModel.$parsers.push(function(value){
var viewValue;
if(angular.isUndefined(value)){
viewValue = "";
}else{
viewValue = "" + value;
}
viewValue = viewValue[0].toUpperCase() + viewValue.substring(1);
//設(shè)置界面內(nèi)容
$ngModel.$setViewValue(viewValue);
//渲染到界面上,這個函數(shù)很重要
$ngModel.$render();
return value;
})
}
}
});

這里我們使用了$setViewValue和$render,$setViewValue設(shè)置viewValue為指定的值,$render把viewValue顯示到界面上。

很多人以為使用了$setViewValue就能更新界面了,沒有使用$render,最后不管怎么搞,界面都沒刷新。

如果只使用了$ngModel.$parsers是不夠的,$parsers只在用戶在輸入框輸入新內(nèi)容的時候觸發(fā),還有一種情況是需要重新刷新輸入框的內(nèi)容的:

那就是雙向綁定,例如剛才的輸入框綁定的是MainController中的$scope.name,當用戶通過其他方式把$scope.name改成"hello",輸入框中看不到首字母大寫。

這時候就要使用$formatters,還是先看個例子吧.

<body ng-controller="MainController">
<form name="form" novalidate="novalidate">
<button ng-click="random()">隨機</button>
<input name="text" type="text" ng-model="name" upper-case>
</form>
</body>

MainController的內(nèi)容:

angular.module("app", [])
.controller("MainController", function ($scope, $timeout) {
$scope.random = function(){
$scope.name = "hello" + Math.random();
}
})

夠簡單吧,點擊按鈕的時候,$scope.name變成hello開頭的隨機內(nèi)容.

很明顯,hello的首字母沒大寫,不是我們想要的內(nèi)容。

我們修改下指令的內(nèi)容:

.directive("upperCase",function(){
return {
restrict:"A",
require:"ngModel",
link:function($scope,$element,$attrs,$ngModel){
$ngModel.$parsers.push(function(value){
var viewValue = upperCaseFirstWord(handleEmptyValue(value));
//設(shè)置界面內(nèi)容
$ngModel.$setViewValue(viewValue);
//渲染到界面上,這個函數(shù)很重要
$ngModel.$render();
return value;
})
//當過外部設(shè)置modelValue的時候,會自動調(diào)用$formatters里面函數(shù)
$ngModel.$formatters.push(function(value){
return upperCaseFirstWord(handleEmptyValue(value));
})
//防止undefined,把所有的內(nèi)容轉(zhuǎn)換成字符串
function handleEmptyValue(value){
return angular.isUndefined(value) ? "" : "" + value;
}
//首字母大寫
function upperCaseFirstWord(value){
return value.length > 0 ? value[0].toUpperCase() + value.substring(1) : "";
}
}
}
});

總結(jié)一下:

1.
-->用戶在輸入框輸入內(nèi)容

-->angular遍歷$ngModel.$parsers里面的函數(shù)轉(zhuǎn)換輸入的內(nèi)容,然后設(shè)置到$ngModel.$modelValue

-->在$ngModel.$parsers數(shù)組中的函數(shù)里,我們修改了$ngModel.$viewValue,然后$ngMode.$render()渲染內(nèi)容。

2.

-->通過按鈕生成隨機的字符串設(shè)置到name

-->每次臟檢測都會判斷name的值是否跟$ngModel.$modelValue不一致(這里是使用$watch實現(xiàn)的),不一致就反序遍歷$formaters里面的所有函數(shù)并執(zhí)行,把最終返回值賦值到$ngModel.$viewValue

-->刷新輸入框內(nèi)容

“自定義輸入框的顯示內(nèi)容”的例子能不能優(yōu)化?

為什么要優(yōu)化?

原因很簡單,為了實現(xiàn)“自定義內(nèi)容”,使用了$parsers和$formatters,其實兩者的內(nèi)容很像!這一點很關(guān)鍵。

怎么優(yōu)化?

使用$ngModel.$validators。

好,現(xiàn)在把例子再改一下。

.directive("upperCase",function(){
return {
restrict:"A",
require:"ngModel",
link:function($scope,$element,$attrs,$ngModel){
//1.3才支持,不管手動輸入還是通過其他地方更新modelValue,都會執(zhí)行這里
$ngModel.$validators.uppercase = function(modelValue,viewValue){
var viewValue = upperCaseFirstWord(handleEmptyValue(modelValue));
//設(shè)置界面內(nèi)容
$ngModel.$setViewValue(viewValue);
//渲染到界面上,這個函數(shù)很重要
$ngModel.$render();
//返回true,表示驗證通過,在這里是沒啥意義
return true;
}
//防止undefined,把所有的內(nèi)容轉(zhuǎn)換成字符串
function handleEmptyValue(value){
return angular.isUndefined(value) ? "" : "" + value;
}
//首字母大寫
function upperCaseFirstWord(value){
return value.length > 0 ? value[0].toUpperCase() + value.substring(1) : "";
}
}
}
})

代碼簡潔了很多,$ngModel.$validators在1.3以上的版本才支持。

$ngModel.$validators.uppercase函數(shù)的返回值如果是false,那么$ngModel.$error['uppercase']=true。這一點跟$ngModel.$setValidity("uppercase",false)差不多。

相關(guān)文章

最新評論