在HTML中,用戶通過input
, select
, textarea
等元素進行輸入,我們通常用表單來包裝和管理這些控件。客戶端表單驗證非常重要,可以及時地爲用戶提供表單驗證信息。但客戶端表單驗證只是爲了增強用戶體驗,服務器端驗證仍然是必要的。
AngularJS最大的特點便是數據綁定。利用Angular在客戶端腳本中構建MVC框架,Model和View之間可以實現雙向綁定。因此AngularJS的表單驗證可以做到實時的用戶反饋。
事實上,正是因爲實時的用戶反饋這個神奇的特性,我們團隊在 http://tianmaying.com 中也繼續引入了AngularJS,儘管此時我們對單頁應用已經不感興趣。
一個簡單的表單
Angular是模塊化的,每個APP都是一個Angular Module。我們知道Module下可以包含這樣四種內容:
- 控制器(controllers),用來完成頁面邏輯,不包含DOM操作、資源獲取。
- 服務(services),用來提供資源訪問和獲取,控制資源的訪問,維護數據一致性。
- 過濾器(filters),用來格式化數據顯示,很多第三方插件以提供
filter
爲主,例如angular-moment
。 - 語義標籤(directives),增強的HTML標籤,DOM操作都應當抽象爲
directive
。
Angular表單其實是Angular提供的Directive,它有一個別名叫ng-form
。是這個Directive實例化了一個FormController
來負責表單內的頁面邏輯(主要是表單驗證)。
<div ng-app>
<ng-form name=someForm>
<input name="username" type="text" ng-model="user.username" pattern="^\w{6,18}$">
<div class="alert alert-danger" ng-show="someForm.username.$error.pattern">
用戶名必須爲6-18個字母、數字或下劃線
</div>
</ng-form>
</div>
ng-model
可以把input
的值雙向地綁定到當前上下文的user.username
變量。我們設置了用戶名的pattern
爲6到18位。我們輸入用戶名時,.alert
錯誤提示便會實時地顯示或者隱藏。
這裏我們指定了
form
的name
屬性,form
Directive 實例化的FormController
就會以someForm
命名,並插入到當前$scope
。所以在模板中才能夠訪問userForm
變量。另外,Angular的Pattern使用Javascript正則表達式語法,這裏\w
相當於[a-zA-Z_]
。
SELECT標籤
HTML中的select
標籤是一個單選的下拉列表,Angular對select
也提供了支持(事實上,是在ng
Module裏面提供了一個叫select
的Directive)。假如上下文中有這樣的對象:
$scope.selectValue = [{value: 0, label: 'Banana'}, {value: 1, label: 'Apple'}];
$socpe.selectedValue = 1;
在模板中這樣寫:
<select ng-model="selectedValue"
ng-options="option.value as option.label for option in myOptions"></select>
這個<select>
的便會有兩個選項:Banana
和Apple
,且默認選中Banana
。當你選擇Apple
時,$socpe.selectedValue
會被賦值爲1
。option.value
指定了<select>
下<option>
的value
,而option.label
指定了<option>
的內容。
事實上,因爲
select
下拉項的樣式不可通過CSS控制,select
在追求視覺體驗的網站不常使用。Bootstrap的.dropdown
就是一個更好的替代品。Angular也有類似的Dropdown插件。
表單嵌套
多數瀏覽器不允許form
嵌套,如果你出於自身的需求(例如:在賬號表單中,頭像表單需要單獨提交)需要嵌套的表單,請使用ng-form
標籤:
<ng-form name="outterForm">
<ng-form name="innerForm" ng-repeat="file in doc.files">
...
<button ng-disabled="innerForm.$invalid">Save Inner</button>
</ng-form>
<button ng-disabled="outterForm.$invalid || innerForm.$invalid">Save Outter</button>
</ng-form>
這裏的outterForm
下有一個動態的innerForm
列表,
- 在
innerForm
下的元素$scope
中是當前列表項的innerForm
。因此Save Inner的狀態會根據正確地綁定到當前表單的狀態。 - 在
outterForm
下的Save Outter則會同時綁定outterForm
和innerForm
的狀態,當所有innerForm
合法且outterForm
合法時,按鈕被激活。
至於你自己的Directive希望通過屬性的方式來啓用還是通過標籤的方式來啓用,可以在你的Directive中設置
restrict
字段。
漸進呈現
在頁面載入時,由於Angular的控制器仍爲完成構造過程,表單會短暫地顯示爲原始的HTML,比如:
當然你能想到最直接的解決方案是給表單加一個隱藏的樣式,在載入後去掉它。然而Angular已經提供ngCloak
Directive來完成這件事情,我們只需要在表單上加一個ng-cloak
:
<form ng-cloak>...</form>
ng-cloak
可以直接加在body
上,但在載入過程中,整個body都會隱藏。這與HTML的漸進呈現的原則是相悖的。建議在表單上單獨地應用ng-cloak
。
HTML屬於流式文檔,已載入的部分的呈現方式總是已知的。在帶寬小的情況下,HTML會逐步顯示已載入的部分。