表單控件(input, select, textarea )是用來獲取用戶輸入的。表單則是一組有聯繫的表單控件的集合。
用戶能通過表單和表單控件提供驗證的服務,知道自己的輸入是否合法。這樣能讓用戶交互變得友好,因爲用戶能通過反饋來修正自己的錯誤。不過,雖然客戶端的驗證能夠起到很大作用,但也很容易被繞過,所以不能完全依靠客戶端驗證。 要建立安全的應用,服務器端驗證還是必不可少的。
簡單表單
瞭解AngularJS雙向綁定的關鍵在於瞭解ngModel
指令。這個指令通過動態將model和view互相映射,來實現雙向綁定。此外它還提供API給其它指令來增強它們的行爲。formatDate
index.html:
<!doctype html>
<html ng-app>
<head>
<script src="http://code.angularjs.org/angular-1.0.2.min.js"></script>
<script src="script.js"></script>
</head>
<body>
<div ng-controller="Controller">
<form novalidate class="simple-form">
Name: <input type="text" ng-model="user.name" /><br />
E-mail: <input type="email" ng-model="user.email" /><br />
Gender: <input type="radio" ng-model="user.gender" value="male" />male
<input type="radio" ng-model="user.gender" value="female" />female<br />
<button ng-click="reset()">RESET</button>
<button ng-click="update(user)">SAVE</button>
</form>
<pre>form = {{user | json}}</pre>
<pre>master = {{master | json}}</pre>
</div>
</body>
</html>
script.js:
function Controller($scope) {
$scope.master= {};
$scope.update = function(user) {
$scope.master= angular.copy(user);
};
$scope.reset = function() {
$scope.user = angular.copy($scope.master);
};
$scope.reset();
}
注意例子中的novalidate
是用來屏蔽瀏覽器本身的驗證功能的。
使用css的類名
爲了能美化表單和表單元素,ngModel指令會自動爲元素添加以下css類:
- ng-valid
- ng-invalid
- ng-pristine
- ng-dirty
下面的例子演示瞭如何使用CSS來顯示錶單的驗證結果。其中的user.name和user.email是必填項,它們沒有被填寫就提交時,底色會變紅。這能避免用戶注意力一開始就被分散。只有在與界面交互之後才顯示錯誤。
index.html:
<!doctype html>
<html ng-app>
<head>
<script src="http://code.angularjs.org/angular-1.0.2.min.js"></script>
<script src="script.js"></script>
</head>
<body>
<div ng-controller="Controller">
<form novalidate class="css-form">
Name:
<input type="text" ng-model="user.name" required /><br />
E-mail: <input type="email" ng-model="user.email" required /><br />
Gender: <input type="radio" ng-model="user.gender" value="male" />male
<input type="radio" ng-model="user.gender" value="female" />female<br />
<button ng-click="reset()">RESET</button>
<button ng-click="update(user)">SAVE</button>
</form>
</div>
<style type="text/css">
.css-form input.ng-invalid.ng-dirty {
background-color: #FA787E;
}
.css-form input.ng-valid.ng-dirty {
background-color: #78FA89;
}
</style>
</body>
</html>
script.js:
function Controller($scope) {
$scope.master= {};
$scope.update = function(user) {
$scope.master= angular.copy(user);
};
$scope.reset = function() {
$scope.user = angular.copy($scope.master);
};
$scope.reset();
}
與表單的狀態或者表單元素狀態綁定
一個表單就是一個FormController
的實例。表單實例可以通過name屬性選擇性地公開到作用域中。同樣的,一個表單控件也是一個NgModelController
的實例。表單控件也能同樣的被公開到作用域中。這意味視圖能通過綁定的基本功能獲取表單或者表單控件的狀態。
這些特點讓我們能將上面的例子改造成下面這樣:
- 只有當表單發生改變時,重置按鈕纔會被顯示出來。
- 只有當表單有改變並且改變的值都是合法的,保存按鈕纔會被顯示出來。
- 能自定義user.email和user.agree的錯誤信息。
index.html:
<!doctype html>
<html ng-app>
<head>
<script src="http://code.angularjs.org/angular-1.0.2.min.js"></script>
<script src="script.js"></script>
</head>
<body>
<div ng-controller="Controller">
<form name="form" class="css-form" novalidate>
Name:
<input type="text" ng-model="user.name" name="uName" required /><br />
E-mail:
<input type="email" ng-model="user.email" name="uEmail" required/><br />
<div ng-show="form.uEmail.$dirty && form.uEmail.$invalid">Invalid:
<span ng-show="form.uEmail.$error.required">Tell us your email.</span>
<span ng-show="form.uEmail.$error.email">This is not a valid email.</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>
</div>
</body>
</html>
script.js:
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();
}
自定義驗證
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來改變表單的驗證狀態。
下面的例子中,我們創建了兩個指令。
-
第一個要驗證的是輸入是否是整數。例如,1.23就不是符合驗證的值,因爲它包含了分數部分。注意,我們是將驗證數組中的項從頭取出,而不是壓入。這是因爲我們要在輸入值被改變(格式化)前,先驗證輸入的合法性。
-
第二個指令是一個“智能浮點(smart-float)”。它能把"1.2"或者"1,2"都轉化爲合法的浮點數"1.2"。注意,這裏我們不能使用“數字輸入類型”,因爲H支持TML5的瀏覽器不允許用戶輸入像"1,2"這樣的非法值。
index.html:
<!doctype html>
<html ng-app="form-example1">
<head>
<script src="http://code.angularjs.org/angular-1.0.2.min.js"></script>
<script src="script.js"></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 />
<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 />
<span ng-show="form.length.$error.float">
This is not a valid float number!</span>
</div>
</form>
</div>
</body>
</html>
script.js:
var app = angular.module('form-example1', []);
var INTEGER_REGEXP = /^\-?\d*$/;
app.directive('integer', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function(viewValue) {
if (INTEGER_REGEXP.test(viewValue)) {
// it is valid
ctrl.$setValidity('integer', true);
return viewValue;
} else {
// it is invalid, return undefined (no model update)
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;
}
});
}
};
});
實現自定義的表單控件(用ngModel)
AngularJS已經實現了所有基本的HTML表單控件(input,select,textarea),對於大部分情況應該已經夠了。但是,你還是可以通過寫指令來實現你自己的表單控件。
要和ngModel指令協同工作實現自定義控件,並且實現雙向綁定的話,需要:
- 實現render方法。這個方法負責在數據傳遞給NgModelController#$formatters後來渲染數據。
- 在用戶與控件交互並且模型需要被更新時,調用$setViewValue方法。這通常是在DOM的監聽事件中完成的。(查看$compileProvider.directive來獲取更多信息)
下面的例子演示瞭如何添加一個“內容可編輯”的數據雙向綁定的元素。
index.html:
<!doctype html>
<html ng-app="form-example2">
<head>
<script src="http://code.angularjs.org/angular-1.0.2.min.js"></script>
<script src="script.js"></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>
script.js:
angular.module('form-example2', []).directive('contenteditable', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
// view -> model
elm.bind('blur', function() {
scope.$apply(function() {
ctrl.$setViewValue(elm.html());
});
});
// model -> view
ctrl.$render = function(value) {
elm.html(value);
};
// load init value from DOM
ctrl.$setViewValue(elm.html());
}
};
});