表單是商業(yè)應(yīng)用的支柱,我們用它來(lái)執(zhí)行登錄、求助、下單、預(yù)訂機(jī)票、安排會(huì)議,以及不計(jì)其數(shù)的其它數(shù)據(jù)錄入任務(wù)。
在開(kāi)發(fā)表單時(shí),創(chuàng)建數(shù)據(jù)方面的體驗(yàn)是非常重要的,它能指引用戶(hù)明細(xì)、高效的完成工作流程。
開(kāi)發(fā)表單需要設(shè)計(jì)能力(那超出了本章的范圍),而框架支持雙向數(shù)據(jù)綁定、變更檢測(cè)、驗(yàn)證和錯(cuò)誤處理,在本章你將會(huì)學(xué)習(xí)它們。
本章展示了如何從草稿構(gòu)建一個(gè)簡(jiǎn)單的表單。在這個(gè)過(guò)程中你將學(xué)會(huì)如何:
- 用組件和模板構(gòu)建 Angular 表單。
- 用
ngModel創(chuàng)建雙向數(shù)據(jù)綁定,以讀取和寫(xiě)入輸入控件的值。 - 跟蹤狀態(tài)的變化,并驗(yàn)證表單控件。
- 使用特殊的 CSS 類(lèi)來(lái)跟蹤控件的狀態(tài)并給出視覺(jué)反饋。
- 向用戶(hù)顯示驗(yàn)證錯(cuò)誤提示,以及啟用/禁用表單控件。
- 使用模板引用變量在 HTML 元素之間共享信息。
模板驅(qū)動(dòng)表單
你可以使用 Angular 模板語(yǔ)法編寫(xiě)模板,結(jié)合本章所描述的表單專(zhuān)用指令和技術(shù)來(lái)構(gòu)建表單。
你還可以使用響應(yīng)式(也叫模型驅(qū)動(dòng))的方式來(lái)構(gòu)建表單。不過(guò)本章中只介紹模板驅(qū)動(dòng)表單。
利用 Angular 模板,可以構(gòu)建幾乎所有表單——登錄表單、聯(lián)系人表單, 以及任何非常漂亮的商務(wù)表單。可以創(chuàng)造性的擺放各種控件、把它們綁定到數(shù)據(jù)、指定校驗(yàn)規(guī)則、顯示校驗(yàn)錯(cuò)誤、有條件的禁用或啟用特定的控件、觸發(fā)內(nèi)置的視覺(jué)反饋等等,不勝枚舉。
Angular 通過(guò)處理大量重復(fù)的、模板化的任務(wù),簡(jiǎn)化了過(guò)程,從而使你不必陷入與自己的斗爭(zhēng)中。
你將學(xué)習(xí)構(gòu)建如下的“模板驅(qū)動(dòng)”表單:

英雄職業(yè)介紹所,使用這個(gè)表單來(lái)維護(hù)英雄們的個(gè)人信息。每個(gè)英雄都需要一份工作。公司的使命就是讓合適的英雄去應(yīng)對(duì)合適的危機(jī)。
表單中的三個(gè)字段,其中兩個(gè)是必填的。根據(jù) material design 指南,必填的字段用星號(hào)(*)標(biāo)出。
如果刪除了英雄的名字,表單就會(huì)用醒目的樣式把驗(yàn)證錯(cuò)誤顯示出來(lái)。

注意,提交按鈕被禁用了,而且輸入控件從綠色變?yōu)榱思t色。
你將一小步一小步地構(gòu)建此表單:
- 創(chuàng)建
Hero模型類(lèi)。 - 創(chuàng)建控制此表單的組件。
- 創(chuàng)建具有初始表單布局的模板。
- 使用 ngModel 雙向數(shù)據(jù)綁定語(yǔ)法把數(shù)據(jù)屬性綁定到每個(gè)表單輸入控件。
- 為每個(gè)表單輸入控件添加 ngControl 指令。
- 添加自定義 CSS 來(lái)提供視覺(jué)反饋。
- 顯示和隱藏有效性驗(yàn)證的錯(cuò)誤信息。
- 使用 ngSubmit 處理表單提交。
- 禁用此表單的提交按鈕,直到表單變?yōu)橛行А?/li>
配置
根據(jù)配置的說(shuō)明創(chuàng)建一個(gè)名為forms的新項(xiàng)目。
添加 angular_forms
Angular 表單的功能在 angular_forms 庫(kù)中,它有自己的包,添加包到 pub 依賴(lài)中:
// {quickstart → forms}/pubspec.yaml
dependencies:
angular: ^5.0.0-alpha
+ angular_forms: ^2.0.0-alpha
創(chuàng)建模型
當(dāng)用戶(hù)輸入表單數(shù)據(jù)時(shí),需要捕獲它們的變化,并更新到模型的實(shí)例中。除非知道模型的樣子,否則無(wú)法設(shè)計(jì)表單的布局。
最簡(jiǎn)單的模型是個(gè)“屬性包”,用來(lái)存放關(guān)于應(yīng)用重點(diǎn)的資料。這里使用了描述Hero類(lèi)的三個(gè)必備字段 (id、name、power),和一個(gè)可選字段 (alterEgo)。
在lib目錄,按照已給出的內(nèi)容創(chuàng)建下面的文件:
// lib/src/hero.dart
class Hero {
int id;
String name, power, alterEgo;
Hero(this.id, this.name, this.power, [this.alterEgo]);
String toString() => '$id: $name ($alterEgo). Super power: $power';
}
這是一個(gè)少量需求和零行為的貧血模型。對(duì)演示來(lái)說(shuō)足夠了。
alterEgo是可選的,所以構(gòu)造函數(shù)允許你省略它;注意在[this.alterEgo]中的方括號(hào)。
可以像這樣創(chuàng)建新英雄:
var myHero = new Hero(
42, 'SkyDog', 'Fetch any object at any distance', 'Leslie Rollover');
print('My hero is ${myHero.name}.'); // "My hero is SkyDog."
創(chuàng)建基本的表單
Angular 表單分為兩部分:基于 HTML 的模板 ,以及用來(lái)處理數(shù)據(jù)和用戶(hù)動(dòng)態(tài)交互的組件類(lèi)。先從這個(gè)類(lèi)開(kāi)始,是因?yàn)樗梢院?jiǎn)要說(shuō)明英雄編輯器能做什么。
創(chuàng)建表單組件
根據(jù)已給出的內(nèi)容創(chuàng)建下面的文件:
// lib/src/hero_form_component.dart (v1)
import 'package:angular/angular.dart';
import 'package:angular_forms/angular_forms.dart';
import 'hero.dart';
const List<String> _powers = [
'Really Smart',
'Super Flexible',
'Super Hot',
'Weather Changer'
];
@Component(
selector: 'hero-form',
templateUrl: 'hero_form_component.html',
directives: [coreDirectives, formDirectives],
)
class HeroFormComponent {
Hero model = new Hero(18, 'Dr IQ', _powers[0], 'Chuck Overstreet');
bool submitted = false;
List<String> get powers => _powers;
void onSubmit() => submitted = true;
}
這個(gè)組件沒(méi)有什么特別的地方,沒(méi)有表單相關(guān)的東西,與之前寫(xiě)過(guò)的組件沒(méi)什么不同。
只需要前面章節(jié)中學(xué)過(guò)的 Angular 概念,就可以完全理解這個(gè)組件:
- 這段代碼導(dǎo)入了 Angular 核心庫(kù)以及你剛剛創(chuàng)建的
Hero模型。 -
@Component選擇器hero-form表示可以用<hero-form>元素把這個(gè)表單放進(jìn)父模板。 -
templateUrl屬性指向一個(gè)獨(dú)立的 HTML 模板文件(稍后創(chuàng)建)。 - 從
model和powers定義模擬數(shù)據(jù)。
接下來(lái),你可以注入一個(gè)數(shù)據(jù)服務(wù),以獲取或保存真實(shí)的數(shù)據(jù),或者把這些屬性暴露為輸入屬性和輸出屬性(參見(jiàn)模板語(yǔ)法中的輸入和輸出屬性)來(lái)綁定到一個(gè)父組件。這不是現(xiàn)在需要關(guān)心的問(wèn)題,未來(lái)的更改不會(huì)影響到這個(gè)表單。
修改 app component
AppComponent 是應(yīng)用的根組件,HeroFormComponent 將被放在其中。
使用下面的內(nèi)容替換初始版本:
// lib/app_component.dart
import 'package:angular/angular.dart';
import 'src/hero_form_component.dart';
@Component(
selector: 'my-app',
template: '<hero-form></hero-form>',
directives: [HeroFormComponent],
)
class AppComponent {}
創(chuàng)建初始 HTML 表單模板
使用下面的內(nèi)容創(chuàng)建模板文件:
// lib/src/hero_form_component.html (start)
<div class="container">
<h1>Hero Form</h1>
<form>
<div class="form-group">
<label for="name">Name *</label>
<input type="text" class="form-control" id="name" required>
</div>
<div class="form-group">
<label for="alterEgo">Alter Ego</label>
<input type="text" class="form-control" id="alterEgo">
</div>
<div class="row">
<div class="col-auto">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
<small class="col text-right">* Required</small>
</div>
</form>
</div>
這是一段簡(jiǎn)單的 HTML5 代碼。我們展現(xiàn)了Hero的兩個(gè)字段,name和alterEgo,提供給用戶(hù)在輸入框中輸入。
Name 的<input>控件具有 HTML5 的required屬性;Alter Ego 的<input>控件沒(méi)有,因?yàn)?code>alterEgo是可選的。
在底部添加了一個(gè)具有一些 CSS 類(lèi)的提交按鈕。
你還沒(méi)有用到 Angular。沒(méi)有綁定,沒(méi)有額外的指令,只有布局。
在模板驅(qū)動(dòng)表單中,你只要導(dǎo)入了
angular_forms庫(kù),就不用對(duì)<form>做任何其它的事情來(lái)使用庫(kù)的功能。接下來(lái)你會(huì)看到它的原理。
刷新瀏覽器。你會(huì)看到一個(gè)簡(jiǎn)單的,沒(méi)有樣式的表單。
給表單添加樣式
container和btn類(lèi)都來(lái)自 Bootstrap。Bootstrap 也有特定的表單類(lèi),包括form-control和form-group。它們給表單添加了一點(diǎn)樣式。
Angular 不需要使用 Bootstrap 類(lèi)或任意外部庫(kù)的樣式。Angular 應(yīng)用可以使用任意 CSS 庫(kù)或一點(diǎn)也不用。
在index.html的<head>插入下面的鏈接來(lái)添加 Bootstrap 樣式。
// web/index.html (bootstrap)
<link rel="stylesheet" integrity="sha384-WskhaSGFgHYWDcbwN70/dfYBj47jz9qbsMId/iRN3ewGhXQFZCSftd1LZCfmhktB" crossorigin="anonymous">
刷新瀏覽器。你會(huì)看到一個(gè)帶有樣式的表單。
使用 *ngFor 添加 powers
英雄必須從認(rèn)證過(guò)的固定列表中選擇一項(xiàng)超能力。你在內(nèi)部維護(hù)這個(gè)列表(在HeroFormComponent)。
在表單中添加select,用ngFor把powers列表綁定到列表選項(xiàng),在之前的顯示數(shù)據(jù)一章中使用過(guò)的技術(shù)。
在緊跟著 Alter Ego 組的下方添加如下 HTML:
// lib/src/hero_form_component.html (powers)
<div class="form-group">
<label for="power">Hero Power *</label>
<select class="form-control" id="power" required>
<option *ngFor="let p of powers" [value]="p">{{p}}</option>
</select>
</div>
powers列表中的每一項(xiàng)超能力都會(huì)渲染成<option>標(biāo)簽。 模板輸入變量p在每個(gè)迭代指向不同的超能力,使用雙花括號(hào)插值表達(dá)式語(yǔ)法來(lái)顯示它的名稱(chēng)。
使用 ngModel 雙向數(shù)據(jù)綁定
現(xiàn)在運(yùn)行此應(yīng)用,有點(diǎn)令人失望。

你看不到英雄數(shù)據(jù)因?yàn)檫€沒(méi)有綁定到Hero。在前面的章節(jié)我們知道怎么去做。顯示數(shù)據(jù)介紹了屬性綁定。用戶(hù)輸入展示了如何通過(guò)事件綁定來(lái)監(jiān)聽(tīng) DOM 事件,以及如何用顯示的值更新組件的屬性。
現(xiàn)在,需要同時(shí)進(jìn)行顯示、監(jiān)聽(tīng)和提取。
雖然可以在表單中再次使用這些已知的技術(shù)。但是,你將使用新的[(ngModel)]語(yǔ)法,使表單綁定到模型的工作變得更容易。
找到Name對(duì)應(yīng)的<input>標(biāo)簽,并且像這樣更新它:
// lib/src/hero_form_component.html (name)
<!-- TODO: remove the next diagnostic line -->
<mark>{{model.name}}</mark><hr>
<div class="form-group">
<label for="name">Name *</label>
<input type="text" class="form-control" id="name" required
[(ngModel)]="model.name"
ngControl="name">
</div>
在 form-group 標(biāo)簽前添加用于診斷的插值表達(dá)式,以看清正在發(fā)生什么事。給自己留個(gè)注釋?zhuān)嵝涯阃瓿珊笠瞥?/p>
聚焦到綁定語(yǔ)法:[(ngModel)]="..."上。
現(xiàn)在運(yùn)行應(yīng)用,開(kāi)始在Name 輸入框中鍵入,添加和刪除字符,我們將看到它們從診斷文本中顯示和消失。某一瞬間,它看起來(lái)可能是這樣:

診斷信息可以證明,數(shù)據(jù)確實(shí)從輸入框流動(dòng)到模型,再反向流動(dòng)回來(lái)。
這就是雙向數(shù)據(jù)綁定!。更多信息,參見(jiàn)模板語(yǔ)法章節(jié)的使用 NgModel 雙向綁定。
注意,<input>標(biāo)簽還添加了ngControl指令,并設(shè)置為 "name",表示英雄的名字。使用任何唯一的值都可以,但使用具有描述性的“name”會(huì)更有幫助。當(dāng)在表單組合中使用[(ngModel)]時(shí),必須要定義ngControl指令。
在內(nèi)部,Angular 創(chuàng)建了一些
NgFormControl,并把它們注冊(cè)到NgForm指令,再將該指令附加到<form>標(biāo)簽。每個(gè)NgFormControl都都以你分配給NgFormControl指令的名稱(chēng)注冊(cè)。稍后會(huì)看到更多 NgForm 的信息。
為 Alter Ego 和 Hero Power 添加類(lèi)似的[(ngModel)]綁定和ngControl指令。
使用model替換診斷綁定表達(dá)式。這樣就能確認(rèn)雙向數(shù)據(jù)綁定在整個(gè) Hero 模型上都能正常工作了。
修改之后,這個(gè)表單的核心是這樣的:
// lib/src/hero_form_component.html (controls)
<!-- TODO: remove the next diagnostic line -->
<mark>{{model}}</mark><hr>
<div class="form-group">
<label for="name">Name *</label>
<input type="text" class="form-control" id="name" required
[(ngModel)]="model.name"
ngControl="name">
</div>
<div class="form-group">
<label for="alterEgo">Alter Ego</label>
<input type="text" class="form-control" id="alterEgo"
[(ngModel)]="model.alterEgo"
ngControl="alterEgo">
</div>
<div class="form-group">
<label for="power">Hero Power *</label>
<select class="form-control" id="power" required
[(ngModel)]="model.power"
ngControl="power">
<option *ngFor="let p of powers" [value]="p">{{p}}</option>
</select>
</div>
- 每個(gè) input 元素都有
id屬性,label元素的for屬性用它來(lái)匹配到對(duì)應(yīng)的輸入控件。- 每個(gè) input 元素都有
ngControl指令,這是 Angular 表單注冊(cè)表單控件所必須的。
如果現(xiàn)在運(yùn)行本應(yīng)用,修改每個(gè) Hero 模型的屬性,表單可能顯示如下:

表單頂部的診斷信息證實(shí)了你所做的一切更改都反映在了 model 中。
從模板刪除診斷綁定因?yàn)樗呀?jīng)完成了它的使命。
基于控件狀態(tài)提供視覺(jué)反饋
使用 CSS 和類(lèi)綁定,可以改變表單控件的外觀來(lái)反映它的狀態(tài)。
追蹤控件狀態(tài)
一個(gè) Angular 表單控件可以告訴你,用戶(hù)是否碰過(guò)此控件,值是否發(fā)生改變,以及值是否無(wú)效。
Angular 表單的每個(gè)控件(NgControl)追蹤自身的狀態(tài),并通過(guò)檢查下面的成員字段使?fàn)顟B(tài)可用:
-
dirty和pristine表明控件的值是否發(fā)生改變。 -
touched和untouched表明控件是否被訪問(wèn)。 -
valid反映了控件值的有效性。
控件樣式
valid控件屬性是最引人注意的,因?yàn)楫?dāng)控件的值無(wú)效時(shí),你希望發(fā)出強(qiáng)烈的視覺(jué)信號(hào)。要?jiǎng)?chuàng)建這樣的視覺(jué)反饋,你需要使用 Bootstrap custom-forms 的類(lèi)is-valid和is-invalid。
在Name 的<input>標(biāo)簽添加一個(gè)名為name的模板引用變量。使用name和類(lèi)綁定有條件的指定恰當(dāng)?shù)谋韱斡行缘念?lèi)。
給Name 的<input>標(biāo)簽臨時(shí)添加另一個(gè)名為spy的模板引用變量,用來(lái)顯示輸入框的 CSS 類(lèi)。
// lib/src/hero_form_component.html (name)
<input type="text" class="form-control" id="name" required
[(ngModel)]="model.name"
#name="ngForm"
#spy
[class.is-valid]="name.valid"
[class.is-invalid]="!name.valid"
ngControl="name">
<!-- TODO: remove the next diagnostic line -->
{{spy.className}}
模板引用變量
spy模板引用變量綁定到了<input>DOM元素,然而name變量(#name="ngForm")綁定到了與 input 元素相關(guān)聯(lián)的 NgModel。為什么是 “ngForm”?指令的 exportAs 屬性告訴 Angular 如何鏈接模板引用變量到指令。把
name設(shè)置為 “ngForm” 是因?yàn)?ngModel 指令的exportAs屬性是 “ngForm”。
刷新瀏覽器,遵循下面的步驟:
- 看 Name 輸入框。
- 它有一個(gè)綠色的邊框。
- 它有
form-control和is-valid類(lèi)。
- 添加一些字符來(lái)改變 name。類(lèi)名依然不變。
- 刪除 name。
- 輸入邊框變紅。
-
is-invalid類(lèi)變成了is-valid。
刪除#spy模板引用變量和使用到它的診斷信息。
另一種類(lèi)綁定的方法,可以使用 NgClass 指令給控件添加樣式。首先添加下面的方法來(lái)設(shè)置控件的狀態(tài)依賴(lài) CSS 類(lèi)名:
// lib/src/hero_form_component.dart (setCssValidityClass)
Map<String, bool> setCssValidityClass(NgControl control) {
final validityClass = control.valid == true ? 'is-valid' : 'is-invalid';
return {validityClass: true};
}
使用上面方法返回的 map 值,綁定到 NgClass 指令——更多關(guān)于這個(gè)指令及其替代品的信息請(qǐng)看模板語(yǔ)法章節(jié)。
// lib/src/hero_form_component.html (power)
<select class="form-control" id="power" required
[(ngModel)]="model.power"
#power="ngForm"
[ngClass]="setCssValidityClass(power)"
ngControl="power">
<option *ngFor="let p of powers" [value]="p">{{p}}</option>
</select>
顯示和隱藏驗(yàn)證錯(cuò)誤信息
你可以改進(jìn)表單。Name 輸入框是必填的,清空它會(huì)使輸入框變紅。這表明有些東西錯(cuò)了,但用戶(hù)不知道錯(cuò)在哪里,或者如何糾正。利用控件狀態(tài)來(lái)顯示有用的信息。
使用 valid 和 pristine 狀態(tài)
當(dāng)用戶(hù)刪除姓名時(shí),表單看起來(lái)應(yīng)該是這樣的:

要達(dá)到這個(gè)效果,在緊跟著 Name <input>標(biāo)簽后面添加下面的<div>:
// lib/src/hero_form_component.html (hidden error message)
<div [hidden]="name.valid || name.pristine" class="invalid-feedback">
Name is required
</div>
刷新瀏覽器,刪除輸入框中的Name。錯(cuò)誤信息就顯示出來(lái)了。
基于name控件的狀態(tài),通過(guò)設(shè)置div的 hidden 屬性,顯式地控制錯(cuò)誤信息。
在這個(gè)例子中,當(dāng)控件是 valid 或 pristine 時(shí),隱藏消息。 “pristine” 意味著從它被顯示在表單中開(kāi)始,用戶(hù)還從未修改過(guò)它的值。
用戶(hù)體驗(yàn)取決于開(kāi)發(fā)人員的選擇
有些開(kāi)發(fā)人員會(huì)希望任何時(shí)候都顯示這條消息。如果忽略了
pristine狀態(tài),就會(huì)只在值有效時(shí)隱藏此消息。如果往這個(gè)組件中傳入全新(空)的英雄,或者無(wú)效的英雄,將立刻看到錯(cuò)誤信息 —— 雖然你還什么都沒(méi)做。有些開(kāi)發(fā)人員會(huì)希望只有在用戶(hù)做出無(wú)效的更改時(shí)才顯示這個(gè)消息。 如果當(dāng)控件是 “pristine” 狀態(tài)時(shí)也隱藏消息,就能達(dá)到這個(gè)目的。在往表單中添加新英雄時(shí),將看到這種選擇的重要性。
Alter Ego 是可選項(xiàng),所以不用改它。
英雄的超能力選項(xiàng)是必填的。只要愿意,可以往<select>上添加相同的錯(cuò)誤處理。但沒(méi)有必要,這個(gè)選擇框已經(jīng)限制了“超能力”只能選有效值。
添加清除按鈕
在組件類(lèi)中添加clear()方法:
// lib/src/hero_form_component.dart (clear)
void clear() {
model.name = '';
model.power = _powers[0];
model.alterEgo = '';
}
在 提交 按鈕的右邊添加一個(gè)綁定click事件的 Clear 按鈕:
// lib/src/hero_form_component.html (Clear button)
<button (click)="clear()" type="button" class="btn">
Clear
</button>
刷新瀏覽器。點(diǎn)擊 Clear 按鈕。文本域都被清空,如果之前改變了Power 的值,也會(huì)重置為默認(rèn)值。
使用 ngSubmit 提交表單
在填表完成之后,用戶(hù)應(yīng)該能提交這個(gè)表單。Submit 按鈕位于表單的底部,它自己不做任何事,但因?yàn)樗?type 值 (type="submit"),所以會(huì)觸發(fā)表單提交。
此時(shí)提交表單是無(wú)意義的。為了讓它有意義,指定表單組件的onSubmit()方法,綁定到表單的ngSubmit事件綁定:
<form (ngSubmit)="onSubmit()" #heroForm="ngForm">
注意模板引用變量#heroForm。正如前面所說(shuō)的,變量heroForm被綁定到管理整個(gè)表單的NgForm指令。
NgForm指令
Angular 自動(dòng)創(chuàng)建并添加 NgForm 指令到
<form>標(biāo)簽。
NgForm指令給表單元素附加了額外的特性。它會(huì)控制那些帶有ngModel和ngControl指令的控件元素,監(jiān)聽(tīng)他們的屬性,包括其有效性。
你要把表單的總體有效性通過(guò)heroForm變量綁定到按鈕的disabled屬性上。
<button [disabled]="!heroForm.form.valid" type="submit" class="btn btn-primary">
Submit
</button>
刷新瀏覽器。你會(huì)發(fā)現(xiàn)按鈕是可用的——盡管它還沒(méi)有做任何有用的事。
現(xiàn)在如果我們刪除 Name,就違反了“required”規(guī)則,它會(huì)恰當(dāng)?shù)娘@示在錯(cuò)誤信息中。提交 按鈕也被禁用了。
不覺(jué)得了不起嗎?再想一想。如果沒(méi)有 Angular 的幫助,我們又該怎樣讓按鈕的禁用/啟用狀態(tài)和表單的有效性關(guān)聯(lián)起來(lái)呢?
有了 Angular,它就是這么簡(jiǎn)單:
- 在(增強(qiáng)的)form 元素上定義一個(gè)模板引用變量。
- 在許多行之外的按鈕上引用該變量。
顯示模型 (可選)
此時(shí)提交表單沒(méi)有視覺(jué)特效。
改進(jìn) demo 也無(wú)法教給我們?nèi)魏侮P(guān)于表單的新知識(shí)。但這是一個(gè)練習(xí)新學(xué)到的綁定技能的好機(jī)會(huì)。如果你不感興趣,跳到本章的總結(jié)部分。
作為視覺(jué)效果,隱藏掉數(shù)據(jù)輸入?yún)^(qū)域并顯示一些其它東西。
把表單包裹進(jìn)<div>中,并把它的hidden屬性綁定到HeroFormComponent.submitted屬性。
// lib/src/hero_form_component.html (excerpt)
<div [hidden]="submitted">
<h1>Hero Form</h1>
<form (ngSubmit)="onSubmit()" #heroForm="ngForm">
</form>
</div>
表單從一開(kāi)始就是可見(jiàn)的,因?yàn)?code>submitted屬性是 false,直到我們提交了這個(gè)表單,正如下面這段HeroFormComponent的代碼展示的:
// lib/src/hero_form_component.dart (submitted)
bool submitted = false;
void onSubmit() => submitted = true;
現(xiàn)在,在剛寫(xiě)的<div>包裹層下方,添加如下 HTML:
// lib/src/hero_form_component.html (submitted)
<div [hidden]="!submitted">
<h1>Hero data</h1>
<table class="table">
<tr>
<th>Name</th>
<td>{{model.name}}</td>
</tr>
<tr>
<th>Alter Ego</th>
<td>{{model.alterEgo}}</td>
</tr>
<tr>
<th>Power</th>
<td>{{model.power}}</td>
</tr>
</table>
<button (click)="submitted=false" class="btn btn-primary">Edit</button>
</div>
刷新瀏覽器,并提交表單。submitted標(biāo)記變成 true,表單消失了。你會(huì)看到英雄模型的值(只讀)顯示在表格中。

這個(gè)視圖包含一個(gè) Edit 按鈕,它的點(diǎn)擊事件綁定清除submitted標(biāo)志。當(dāng)你點(diǎn)擊 Edit 按鈕時(shí),表格消失,可編輯的表單又出現(xiàn)了。
總結(jié)
Angular 表單提供了數(shù)據(jù)修改、驗(yàn)證等支持。在本章中學(xué)到了怎樣使用下面的特性:
- 一個(gè) HTML 表單模板和一個(gè)帶有
@Component注解的表單組件類(lèi)。 - 通過(guò)一個(gè)
ngSubmit事件綁定處理表單提交。 - 模板引用變量,例如
heroForm和name。 - 雙向數(shù)據(jù)綁定(
[(ngModel)])。 - 用于驗(yàn)證和追蹤表單元素變化的
ngControl指令。 - input 控件的
valid屬性(通過(guò)模板引用變量獲?。?,用于檢查控件的有效性和顯示/隱藏錯(cuò)誤信息。 - NgForm.form的有效性來(lái)設(shè)置 提交 按鈕的激活狀態(tài)。
- 定制 CSS 類(lèi)來(lái)給用戶(hù)提供控件狀態(tài)的視覺(jué)反饋。
下一步