表單控件(input, select, textarea )是用來獲取用戶輸入的。表單則是一組有聯系的表單控件的集合。
用戶能通過表單和表單控件提供驗證的服務,知道自己的輸入是否合法。這樣能讓用戶交互變得友好,因為用戶能通過反饋來修正自己的錯誤。不過,雖然客戶端的驗證能夠起到很大作用,但也很容易被繞過,所以不能完全依靠客戶端驗證。 要建立安全的應用,服務器端驗證還是必不可少的。
了解AngularJS雙向綁定的關鍵在於了解ngModel指令。這個指令通過動態將model和view互相映射,來實現雙向綁定。
為了能美化表單和表單元素,ngModel指令會自動為元素添加以下css類:
- ng-valid
- ng-invalid
- ng-pristine
- ng-dirty
一個表單就是一個FormController的實例,表單實例可以通過name屬性選擇性地公開到作用域中。同樣的,一個表單控件也是一個NgModelController的實例,表單控件也能同樣的被公開到作用域中。這意味視圖能通過綁定的基本功能獲取表單或者表單控件的狀態。
- 只有當表單發生改變時,重置按鈕才會被顯示出來。
- 只有當表單有改變並且改變的值都是合法的,保存按鈕才會被顯示出來。
- 能自定義user.email和user.agree的錯誤信息。
舉個例子:
<!doctype html>
<html ng-app>
<head>
<script src="http://code.angularjs.org/angular-1.0.2.min.js"></script>
<script>
function Controller($scope) {
$scope.master= {};
$scope.update = function(user) {
$scope.master= angular.copy(user);
};
$scope.reset = function() {
$scope.user = angular.copy($scope.master);
};
$scope.isUnchanged = function(user) {
return angular.equals(user, $scope.master);
};
$scope.reset();
}
</script>
</head>
<body>
<div ng-controller="Controller">
<form name="form" class="css-form" novalidate> //novalidate是用來屏蔽瀏覽器本身的驗證功能的。
Name:
<input type="text" ng-model="user.name" name="uName" required /><br /> //required,此輸入框必須有內容
E-mail:
<input type="email" ng-model="user.email" name="uEmail" required/><br />
<div ng-show="form.uEmail.$dirty && form.uEmail.$invalid">Invalid: //此div,如果form表中的name為uEmail的input元素中的內容違法或者是臟數據,那么就顯示出來。
<span ng-show="form.uEmail.$error.required">Tell us your email.</span> //如果錯誤信息是由required引起的,就顯示此span
<span ng-show="form.uEmail.$error.email">This is not a valid email.</span> //如果錯誤信息是由里面的內容不合法引起的,就顯示此span
</div>
Gender: <input type="radio" ng-model="user.gender" value="male" />male
<input type="radio" ng-model="user.gender" value="female" />female<br />
<input type="checkbox" ng-model="user.agree" name="userAgree" required />
I agree: <input ng-show="user.agree" type="text" ng-model="user.agreeSign"
required /><br />
<div ng-show="!user.agree || !user.agreeSign">Please agree and sign.</div>
<button ng-click="reset()" ng-disabled="isUnchanged(user)">RESET</button>
<button ng-click="update(user)" ng-disabled="form.$invalid || isUnchanged(user)">SAVE</button> //如果整個form表單沒有通過驗證或者form表單中的內容沒有發生改變,此按鈕就不可點擊
</form>
</div>
</body>
</html>
AngularJS實現了大部分常見的html5表單輸入元素(text, number, url, email, radio, checkbox),也實現了很多用於驗證的指令(required, pattern, minlength, maxlength, min, max)。
如果想要定義你自己的驗證器的話,可以通過在你自己的指令中添加一個驗證函數給ngModel的控制器來實現。要想獲得控制器的引用,需要在指令中指定依賴,驗證函數可以寫在兩個地方。
-
模型到視圖的更新中- 只要綁定的模型改變了,NgModelController#$formatters數組中的函數就會被輪流調用,所以每一個函數都有機會對數據進行格式化,當然你也可以通過NgModelController#$setValidity來改變表單的驗證狀態。
-
視圖到模型的更新中- 相同的,只要用戶與表單交互了,NgModelController#$setViewValue就會被調用。 這次是NgModelController#$parsers數組中的函數會被依次調用,每個函數都有機會來改變值或者通過NgModelController#$setValidity來改變表單的驗證狀態。下面的例子,使用的是這個。
舉個例子:
<!doctype html>
<html ng-app="form-example1">
<head>
<script src="http://code.angularjs.org/angular-1.0.2.min.js"></script>
<script>
var app = angular.module('form-example1', []);
var INTEGER_REGEXP = /^\-?\d+$/; //或者以"-"(負)開頭,或者沒有負號,后面都是正整數,包括0
app.directive('integer', function() {
return {
require: 'ngModel', //依賴nogModel指令
link: function(scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function(viewValue) { //ctrl可以調用ngModel指令中controller函數中定義的方法
if (INTEGER_REGEXP.test(viewValue)) {
ctrl.$setValidity('integer', true);
return viewValue;
} else {
ctrl.$setValidity('integer', false);
return undefined;
}
});
}
};
});
var FLOAT_REGEXP = /^\-?\d+((\.|\,)\d+)?$/; //可以是正負,可以是整數,也可以是浮點數,浮點數可以用"."分開,也可以用","分開。
app.directive('smartFloat', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function(viewValue) {
if (FLOAT_REGEXP.test(viewValue)) {
ctrl.$setValidity('float', true);
return parseFloat(viewValue.replace(',', '.'));
} else {
ctrl.$setValidity('float', false);
return undefined;
}
});
}
};
});
</script>
</head>
<body>
<div ng-controller="Controller">
<form name="form" class="css-form" novalidate>
<div>
Size (integer 0 - 10):
<input type="number" ng-model="size" name="size" min="0" max="10" integer />{{size}}<br /> //integer,自定義指令
<span ng-show="form.size.$error.integer">This is not valid integer!</span>
<span ng-show="form.size.$error.min || form.size.$error.max">The value must be in range 0 to 10!</span>
</div>
<div>
Length (float):
<input type="text" ng-model="length" name="length" smart-float />{{length}}<br /> //smart-float,自定義指令
<span ng-show="form.length.$error.float">This is not a valid float number!</span> </div> </form> </div> </body> </html>
上面的例子中,我們創建了兩個指令。
-
第一個指令是要驗證輸入的是否是整數。例如,1.23就不符合驗證的值,因為它包含了分數部分。
-
第二個指令是要驗證輸入的是否是“智能浮點(smart-float)”。它能把"1.2"或者"1,2"都轉化為合法的浮點數"1.2"。注意,這里我們不能使用input元素的type="number",因為支持HTML5的瀏覽器不允許用戶輸入像"1,2"這樣的非法值。
AngularJS已經實現了所有基本的HTML表單控件(input,select,textarea),對於大部分情況應該已經夠了。但是,你還是可以通過寫指令來實現你自己的表單控件。
要和ngModel指令協同工作實現自定義控件,並且實現雙向綁定的話,需要:
- 實現render方法。這個方法負責在數據模型變化時,把變化的值傳遞給NgModelController#$formatters后,用來在view上渲染新的數據。
- 在用戶與控件交互並且模型需要被更新時,調用$setViewValue方法。這通常是在DOM的監聽事件中完成的。
下面的例子演示了如何添加一個“內容可編輯”的數據雙向綁定的元素。
<!doctype html>
<html ng-app="form-example2">
<head>
<script src="http://code.angularjs.org/angular-1.0.2.min.js"></script>
<script>
angular.module('form-example2', []).directive('contenteditable', function() { //自定義指令contenteditable
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
// view -> model
elm.bind('blur', function() { //給元素div綁定blur事件
scope.$apply(function() {
ctrl.$setViewValue(elm.html()); //當輸入結束后,焦點離開div元素時,就更新model
});
});
// model -> view
ctrl.$render = function(value) {
elm.html(value); //更新視圖view
};
// load init value from DOM
ctrl.$setViewValue(elm.html());
}
};
});
</script>
</head>
<body>
<div contentEditable="true" ng-model="content" title="Click to edit">Some</div>
<pre>model = {{content}}</pre>
<style type="text/css">
div[contentEditable] {
cursor: pointer;
background-color: #D0D0D0;
}
</style>
</body>
</html>
加油!
