響應(yīng)式表單提供了一種模型驅(qū)動的方式來處理表單輸入,其中的值會隨時(shí)間而變化。本文會向你展示如何創(chuàng)建和更新基本的表單控件,接下來還會在一個(gè)表單組中使用多個(gè)控件,驗(yàn)證表單的值,以及創(chuàng)建動態(tài)表單,也就是在運(yùn)行期添加或移除控件。
試試這個(gè)響應(yīng)式表單的現(xiàn)場演練 / 下載范例。
響應(yīng)式表單使用顯式的、不可變的方式,管理表單在特定的時(shí)間點(diǎn)上的狀態(tài)。對表單狀態(tài)的每一次變更都會返回一個(gè)新的狀態(tài),這樣可以在變化時(shí)維護(hù)模型的整體性。響應(yīng)式表單是圍繞 ?Observable
?流構(gòu)建的,表單的輸入和值都是通過這些輸入值組成的流來提供的,它可以同步訪問。
響應(yīng)式表單還提供了一種更直觀的測試路徑,因?yàn)樵谡埱髸r(shí)你可以確信這些數(shù)據(jù)是一致的、可預(yù)料的。這個(gè)流的任何一個(gè)消費(fèi)者都可以安全地操縱這些數(shù)據(jù)。
響應(yīng)式表單與模板驅(qū)動表單有著顯著的不同點(diǎn)。響應(yīng)式表單通過對數(shù)據(jù)模型的同步訪問提供了更多的可預(yù)測性,使用 Observable 的操作符提供了不可變性,并且通過 Observable 流提供了變化追蹤功能。
模板驅(qū)動表單允許你直接在模板中修改數(shù)據(jù),但不像響應(yīng)式表單那么明確,因?yàn)樗鼈円蕾嚽度氲侥0逯械闹噶?,并借助可變?shù)據(jù)來異步跟蹤變化。
使用表單控件有三個(gè)步驟。
FormControl
?實(shí)例,并把它保存在組件中。FormControl
?。然后,你可以把組件添加到模板中來顯示表單。
下面的例子展示了如何添加一個(gè)表單控件。在這個(gè)例子中,用戶在輸入字段中輸入自己的名字,捕獲其輸入值,并顯示表單控件的當(dāng)前值。
動作 |
詳情 |
---|---|
注冊響應(yīng)式表單模塊 |
要使用響應(yīng)式表單控件,就要從
|
生成新的 |
使用 CLI 命令
可以用 ? |
在模板中注冊該控件 |
在組件類中創(chuàng)建了控件之后,你還要把它和模板中的一個(gè)表單控件關(guān)聯(lián)起來。修改模板,為表單控件添加 ?formControl ?綁定,?formControl ?是由 ?ReactiveFormsModule ?中的 ?FormControlDirective ?提供的。 使用這種模板綁定語法,把該表單控件注冊給了模板中名為 ?name ?的輸入元素。這樣,表單控件和 DOM 元素就可以互相通訊了:視圖會反映模型的變化,模型也會反映視圖中的變化。
|
顯示該組件 |
把該組件添加到模板時(shí),將顯示指派給 ?
|
你可以用下列方式顯示它的值。
valueChanges
?,你可以在模板中使用 ?AsyncPipe
?或在組件類中使用 ?subscribe()
? 方法來監(jiān)聽表單值的變化。value
?屬性。它能讓你獲得當(dāng)前值的一份快照。下面的例子展示了如何在模板中使用插值顯示當(dāng)前值。
<p>Value: {{ name.value }}</p>
一旦你修改了表單控件所關(guān)聯(lián)的元素,這里顯示的值也跟著變化了。
響應(yīng)式表單還能通過每個(gè)實(shí)例的屬性和方法提供關(guān)于特定控件的更多信息。?AbstractControl
?的這些屬性和方法用于控制表單狀態(tài),并在處理表單校驗(yàn)時(shí)決定何時(shí)顯示信息。
響應(yīng)式表單還有一些方法可以用編程的方式修改控件的值,它讓你可以靈活的修改控件的值而不需要借助用戶交互。?FormControl
?提供了一個(gè) ?setValue()
? 方法,它會修改這個(gè)表單控件的值,并且驗(yàn)證與控件結(jié)構(gòu)相對應(yīng)的值的結(jié)構(gòu)。比如,當(dāng)從后端 API 或服務(wù)接收到了表單數(shù)據(jù)時(shí),可以通過 ?setValue()
? 方法來把原來的值替換為新的值。
下列的例子往組件類中添加了一個(gè)方法,它使用 ?setValue()
? 方法來修改 Nancy 控件的值。
updateName() {
this.name.setValue('Nancy');
}
修改模板,添加一個(gè)按鈕,用于模擬改名操作。在點(diǎn) ?Update Name
? 按鈕之前表單控件元素中輸入的任何值都會回顯為它的當(dāng)前值。
<button type="button" (click)="updateName()">Update Name</button>
由于表單模型是該控件的事實(shí)之源,因此當(dāng)你單擊該按鈕時(shí),組件中該輸入框的值也變化了,覆蓋掉它的當(dāng)前值。
注意:
在這個(gè)例子中,你只使用單個(gè)控件,但是當(dāng)調(diào)用 ?FormGroup
?或 ?FormArray
?實(shí)例的 ?setValue()
? 方法時(shí),傳入的值就必須匹配控件組或控件數(shù)組的結(jié)構(gòu)才行。
表單中通常會包含幾個(gè)相互關(guān)聯(lián)的控件。響應(yīng)式表單提供了兩種把多個(gè)相關(guān)控件分組到同一個(gè)輸入表單中的方法。
表單組 |
詳情 |
---|---|
表單分組 |
定義了一個(gè)帶有一組控件的表單,你可以把它們放在一起管理。表單組的基礎(chǔ)知識將在本節(jié)中討論。你也可以通過嵌套表單組來創(chuàng)建更復(fù)雜的表單。 |
表單數(shù)組 |
定義了一個(gè)動態(tài)表單,你可以在運(yùn)行時(shí)添加和刪除控件。你也可以通過嵌套表單數(shù)組來創(chuàng)建更復(fù)雜的表單。 |
就像 ?FormControl
?的實(shí)例能讓你控制單個(gè)輸入框所對應(yīng)的控件一樣,?FormGroup
?的實(shí)例也能跟蹤一組 ?FormControl
?實(shí)例(比如一個(gè)表單)的表單狀態(tài)。當(dāng)創(chuàng)建 ?FormGroup
?時(shí),其中的每個(gè)控件都會根據(jù)其名字進(jìn)行跟蹤。下面的例子展示了如何管理單個(gè)控件組中的多個(gè) ?FormControl
?實(shí)例。
生成一個(gè) ?ProfileEditor
?組件并從 ?@angular/forms
? 包中導(dǎo)入 ?FormGroup
?和 ?FormControl
?類。
ng generate component ProfileEditor
import { FormGroup, FormControl } from '@angular/forms';
要將表單組添加到此組件中,請執(zhí)行以下步驟。
FormGroup
?實(shí)例。FormGroup
?模型關(guān)聯(lián)到視圖。
動作 |
詳情 |
---|---|
創(chuàng)建一個(gè) |
在組件類中創(chuàng)建一個(gè)名叫 ? 對此個(gè)人檔案表單,要添加兩個(gè) ?
這些獨(dú)立的表單控件被收集到了一個(gè)控件組中。這個(gè) ? |
把這個(gè) |
這個(gè)表單組還能跟蹤其中每個(gè)控件的狀態(tài)及其變化,所以如果其中的某個(gè)控件的狀態(tài)或值變化了,父控件也會發(fā)出一次新的狀態(tài)變更或值變更事件。該控件組的模型來自它的所有成員。在定義了這個(gè)模型之后,你必須更新模板,來把該模型反映到視圖中。
NOTE: 由 ? |
保存表單數(shù)據(jù) |
?
?
?
注意: |
顯示此組件 |
要顯示包含此表單的 ?
?
|
表單組可以同時(shí)接受單個(gè)表單控件實(shí)例和其它表單組實(shí)例作為其子控件。這可以讓復(fù)雜的表單模型更容易維護(hù),并在邏輯上把它們分組到一起。
如果要構(gòu)建復(fù)雜的表單,如果能在更小的分區(qū)中管理不同類別的信息就會更容易一些。使用嵌套的 ?FormGroup
?可以讓你把大型表單組織成一些稍小的、易管理的分組。
要制作更復(fù)雜的表單,請遵循如下步驟。
某些類型的信息天然就屬于同一個(gè)組。比如名稱和地址就是這類嵌套組的典型例子,下面的例子中就用到了它們。
動作 |
詳情 |
---|---|
創(chuàng)建一個(gè)嵌套的表單組。 |
要在 profileForm 中創(chuàng)建一個(gè)嵌套組,就要把一個(gè)嵌套的 address 元素添加到此表單組的實(shí)例中。
在這個(gè)例子中,? |
在模板中對這個(gè)嵌套表單分組。 |
在修改了組件類中的模型之后,還要修改模板,來把這個(gè) FormGroup 實(shí)例對接到它的輸入元素。 ?ProfileEditor ?表單顯示為一個(gè)組,但是將來這個(gè)模型會被進(jìn)一步細(xì)分,以表示邏輯分組區(qū)域。
提示: |
當(dāng)修改包含多個(gè) ?FormGroup
?實(shí)例的值時(shí),你可能只希望更新模型中的一部分,而不是完全替換掉。這一節(jié)會講解該如何更新 ?AbstractControl
?模型中的一部分。
有兩種更新模型值的方式:
方法 |
詳情 |
---|---|
setValue()
|
使用 |
patchValue()
|
使用 |
?setValue()
? 方法的嚴(yán)格檢查可以幫助你捕獲復(fù)雜表單嵌套中的錯誤,而 ?patchValue()
? 在遇到那些錯誤時(shí)可能會默默的失敗。
在 ?ProfileEditorComponent
?中,使用 ?updateProfile
?方法傳入下列數(shù)據(jù)可以更新用戶的名字與街道住址。
updateProfile() {
this.profileForm.patchValue({
firstName: 'Nancy',
address: {
street: '123 Drew Street'
}
});
}
通過往模板中添加一個(gè)按鈕來模擬一次更新操作,以修改用戶檔案。
<button type="button" (click)="updateProfile()">Update Profile</button>
當(dāng)點(diǎn)擊按鈕時(shí),?profileForm
?模型中只有 ?firstName
?和 ?street
?被修改了。注意,?street
?是在 ?address
?屬性的對象中被修改的。這種結(jié)構(gòu)是必須的,因?yàn)?nbsp;?patchValue()
? 方法要針對模型的結(jié)構(gòu)進(jìn)行更新。?patchValue()
? 只會更新表單模型中所定義的那些屬性。
當(dāng)需要與多個(gè)表單打交道時(shí),手動創(chuàng)建多個(gè)表單控件實(shí)例會非常繁瑣。?FormBuilder
?服務(wù)提供了一些便捷方法來生成表單控件。?FormBuilder
?在幕后也使用同樣的方式來創(chuàng)建和返回這些實(shí)例,只是用起來更簡單。
通過下列步驟可以利用這項(xiàng)服務(wù)。
FormBuilder
?類。FormBuilder
?服務(wù)。下面的例子展示了如何重構(gòu) ?ProfileEditor
?組件,用 ?FormBuilder
?來代替手工創(chuàng)建這些 ?FormControl
?和 ?FormGroup
?實(shí)例。
動作 |
詳情 |
---|---|
導(dǎo)入 FormBuilder 類 |
從 ?@angular/forms ? 包中導(dǎo)入 ?FormBuilder ?類。
|
注入 FormBuilder 服務(wù) |
?
|
生成表單控件 |
?
在上面的例子中,你可以使用 ? 提示: 比較一下用表單構(gòu)建器和手動創(chuàng)建實(shí)例這兩種方式。
|
表單驗(yàn)證用于確保用戶的輸入是完整和正確的。本節(jié)講解了如何把單個(gè)驗(yàn)證器添加到表單控件中,以及如何顯示表單的整體狀態(tài)。
使用下列步驟添加表單驗(yàn)證。
最常見的驗(yàn)證是做一個(gè)必填字段。下面的例子給出了如何在 ?firstName
?控件中添加必填驗(yàn)證并顯示驗(yàn)證結(jié)果的方法。
操作 |
詳情 |
---|---|
導(dǎo)入驗(yàn)證器函數(shù) |
響應(yīng)式表單包含了一組開箱即用的常用驗(yàn)證器函數(shù)。這些函數(shù)接收一個(gè)控件,用以驗(yàn)證并根據(jù)驗(yàn)證結(jié)果返回一個(gè)錯誤對象或空值。 從 ?
|
建一個(gè)必填字段 |
在 ?
|
顯示表單狀態(tài)
|
當(dāng)你往表單控件上添加了一個(gè)必填字段時(shí),它的初始值是無效的(invalid)。這種無效狀態(tài)會傳播到其父 ?FormGroup ?元素中,也讓這個(gè) ?FormGroup ?的狀態(tài)變?yōu)闊o效的。你可以通過該 ?FormGroup ?實(shí)例的 ?status ?屬性來訪問其當(dāng)前狀態(tài)。
提交按鈕被禁用了,因?yàn)?? firstName ?控件的必填項(xiàng)規(guī)則導(dǎo)致了 ?profileForm ?也是無效的。在你填寫了 ?firstName ?輸入框之后,該表單就變成了有效的,并且提交按鈕也啟用了。
|
?FormArray
?是 ?FormGroup
?之外的另一個(gè)選擇,用于管理任意數(shù)量的匿名控件。像 ?FormGroup
?實(shí)例一樣,你也可以往 ?FormArray
?中動態(tài)插入和移除控件,并且 ?FormArray
?實(shí)例的值和驗(yàn)證狀態(tài)也是根據(jù)它的子控件計(jì)算得來的。不過,你不需要為每個(gè)控件定義一個(gè)名字作為 key,因此,如果你事先不知道子控件的數(shù)量,這就是一個(gè)很好的選擇。
要定義一個(gè)動態(tài)表單,請執(zhí)行以下步驟。
FormArray
?類。FormArray
?控件。FormArray
?控件。下面的例子展示了如何在 ?ProfileEditor
?中管理別名數(shù)組。
操作 |
詳情 |
---|---|
導(dǎo)入 ??FormArray?? 類 |
從 ?
|
定義 FormArray 控件 |
你可以通過把一組(從零項(xiàng)到多項(xiàng))控件定義在一個(gè)數(shù)組中來初始化一個(gè) ? 使用 ?
|
訪問 FormArray 控件 |
相對于重復(fù)使用 ?profileForm.get() ? 方法獲取每個(gè)實(shí)例的方式,getter 可以讓你輕松訪問表單數(shù)組各個(gè)實(shí)例中的別名。表單數(shù)組實(shí)例用一個(gè)數(shù)組來代表未定數(shù)量的控件。通過 getter 來訪問控件很方便,這種方法還能很容易地重復(fù)處理更多控件。
使用 getter 語法創(chuàng)建類屬性 ? aliases ?,以從父表單組中接收表示綽號的表單數(shù)組控件。
注意:定義一個(gè)方法來把一個(gè)綽號控件動態(tài)插入到綽號 ? FormArray ?中。用 ?FormArray.push() ? 方法把該控件添加為數(shù)組中的新條目。 在這個(gè)模板中,這些控件會被迭代,把每個(gè)控件都顯示為一個(gè)獨(dú)立的輸入框。
|
在模板中顯示表單數(shù)組 |
要想為表單模型添加 ?aliases ?,你必須把它加入到模板中供用戶輸入。和 ?FormGroupNameDirective ?提供的 ?formGroupName ?一樣,?FormArrayNameDirective ?也使用 ?formArrayName ?在這個(gè) ?FormArray ?實(shí)例和模板之間建立綁定。
在 ? formGroupName ??<div> ? 元素的結(jié)束標(biāo)簽下方,添加一段模板 HTML。 ?*ngFor ? 指令對 ?aliases ??FormArray ?提供的每個(gè) ?FormControl ?進(jìn)行迭代。因?yàn)??FormArray ?中的元素是匿名的,所以你要把索引號賦值給 ?i ? 變量,并且把它傳給每個(gè)控件的 ?formControlName ?輸入屬性。
每當(dāng)新的 ? alias ?加進(jìn)來時(shí),?FormArray ?的實(shí)例就會基于這個(gè)索引號提供它的控件。這將允許你在每次計(jì)算根控件的狀態(tài)和值時(shí)跟蹤每個(gè)控件。
|
添加一個(gè)別名 |
最初,表單只包含一個(gè)綽號字段,點(diǎn)擊 ? 注意: |
下表給出了用于創(chuàng)建和管理響應(yīng)式表單控件的基礎(chǔ)類和服務(wù)。
類 |
詳情 |
---|---|
AbstractControl
|
所有三種表單控件類( |
FormControl
|
管理單體表單控件的值和有效性狀態(tài)。它對應(yīng)于 HTML 的表單控件,比如 |
FormGroup
|
管理一組 |
FormArray
|
管理一些 |
FormBuilder
|
一個(gè)可注入的服務(wù),提供一些用于提供創(chuàng)建控件實(shí)例的工廠方法。 |
指令 |
詳情 |
---|---|
FormControlDirective
|
把一個(gè)獨(dú)立的 |
FormControlName
|
把一個(gè)現(xiàn)有 |
FormGroupDirective
|
把一個(gè)現(xiàn)有的 |
FormGroupName
|
把一個(gè)內(nèi)嵌的 |
FormArrayName
|
把一個(gè)內(nèi)嵌的 |
更多建議: