詳解Angular2響應(yīng)式表單
本文將半翻譯半總結(jié)的講講ng2官網(wǎng)的另一個未翻譯高級教程頁面。
文章目的是使用ng2提供的響應(yīng)式表單技術(shù)快速搭出功能完善豐富的界面表單組件。
響應(yīng)式表單是一項響應(yīng)式風格的ng2技術(shù),本文將解釋響應(yīng)式表單并用來創(chuàng)建一個英雄詳情編輯器。
包含內(nèi)容:
- 響應(yīng)式表單介紹
- 開始搭建
- 創(chuàng)建數(shù)據(jù)模型
- 創(chuàng)建響應(yīng)式的表單組件
- 創(chuàng)建組建的模板文件
- 引入ReactiveFormsModule
- 顯示HeroDetailComponent
- 添加一個FormGroup
- 看看表單模型
- 介紹FormBuilder
- 驗證的需求
- 放置FormGroup
- 檢查FormControl屬性
- 使用setValue以及patchValue設(shè)置表單模型數(shù)據(jù)
- 使用FotmArray提供FormGroup的數(shù)組
- 觀察控件的更改
響應(yīng)式表單介紹
angular提供了兩種表單搭建技術(shù): 響應(yīng)式表單和模板驅(qū)動式表單。都依賴于@angular/forms庫,并共享了一些通用的表單控件集。
但是他們在原理、代碼風格以及技術(shù)上存在區(qū)別。他們甚至有自己的模塊:ReactiveFormsModule以及FormsModule。
響應(yīng)式表單(ReactiveFormsModule):
anguar的響應(yīng)式表單簡化了管理數(shù)據(jù)時響應(yīng)式風格的編碼實現(xiàn),使用了在無視圖數(shù)據(jù)模型(從服務(wù)器獲取)以及以視圖為導向的模型用于保持屏幕上HTML控件顯示的值與狀態(tài)。響應(yīng)式表單提供了響應(yīng)式模式測試以及驗證上的便利。
使用響應(yīng)式表單,你將在組件類中創(chuàng)建一個anular的表單控件樹對象,并在組件模板中使用提供的技術(shù)綁定到原生表單控件標簽中。
你直接在組件類中創(chuàng)建并操作控件對象。因為組件類能直接訪問到數(shù)據(jù)模型以及表單控件結(jié)構(gòu),你可以將數(shù)據(jù)模型值推送到表單控件以及將用戶的更改響應(yīng)到后邊來。組件可以觀察表單控件狀態(tài)的更改并響應(yīng)這些更改。
直接使用表單控件對象工作的一個好處是值以及驗證的更新總能夠同步完成并受你控制。你不會遇到有時候因為模板驅(qū)動表單造成的時間問題,并且響應(yīng)式表單更易測試。
為了保持響應(yīng)的一致性,組件會保存不一致的數(shù)據(jù)模型,將其視為純粹的原始值。不會直接更新數(shù)據(jù)模型,組件會提取用戶的更改并轉(zhuǎn)發(fā)到外面的組件或服務(wù)中,(可能是用來保存他們的)并返回一個新數(shù)據(jù)模型到組件,用于響應(yīng)模型狀態(tài)的更新。
使用響應(yīng)式表單指令不需要你依賴于全部響應(yīng)式原理,但是這確實能促進響應(yīng)式編程方法如果你選擇了要使用這個方法的話。
模板驅(qū)動式表單(FormsModule):
模板驅(qū)動式的表單使用了完全不同的方式。
你在組件模板中放置HTML表單控件(input這些)并使用比如ngModel這些指令綁定到數(shù)據(jù)模型屬性。
你不需要創(chuàng)建angular表單控件對象,因為angular會根據(jù)你的數(shù)據(jù)綁定信息自動幫你創(chuàng)建出來。你不是推送或者拉取數(shù)據(jù)值。angular在ngModel中幫你處理了。angular會更新那些被改變的數(shù)據(jù)值。
出于這個原因,ngModel不再是ReactiveFormsModule的一部分了。
這意味著可以在組件類中寫更少的代碼,不過模板驅(qū)動表單是異步工作的,這可能會在某些情況下復雜化開發(fā)。
同步vs異步
響應(yīng)式表單是同步的。模板驅(qū)動表單是異步的,這是其區(qū)別的根源。
在響應(yīng)式表單中,你在代碼中創(chuàng)建一個完整的表單控件樹。你可以從子表單或父表單中立即更新或取回一個值,因為所有的控件都可訪問到。
模板驅(qū)動表單將他們的表單控件的創(chuàng)建委托給了指令。為了避免“檢查后又更改”的錯誤,這些指令使用了不止一個循環(huán)來建立整個控件樹。這意味著你必須在操作任何組件類中的空間表單時等那么一小會兒。
比如說,如果你使用@ViewChild(NgForm)查詢注入到表單控件中并在ngAfterViewInit這個生命周期鉤子中檢查它,你將發(fā)現(xiàn)它沒有子元素。你必須等一會,使用setTimeout來等待,然后你才能從這個空間中去除值并驗證它或者將它設(shè)置為新的值。
模板驅(qū)動表單的異步性同時復雜化了單元測試。你必須使用async()或者fakeAsync()來包裝你的測試塊來避免找不到表單的值。而如果使用的是響應(yīng)式表單,一切都如你所愿的存在著。
哪一個方式更好?
沒有哪種是更好的。他們是兩種不同的搭建方式,各自擁有長處和短處。使用最適合你的方式才是對的。在一個應(yīng)用中你可能兩種方式都要使用到。
本文僅僅會描述響應(yīng)式的范例與精華所在。對于模板驅(qū)動式表單,可以前往表單介紹頁。
接下來你將寫出你自己的項目來演示響應(yīng)式表單。然后你將學會關(guān)于angular表單類以及如何在響應(yīng)式表單中使用它。
對上文的總結(jié)就是,相比ng1中數(shù)據(jù)的雙向綁定,ng2保留了這個雙向綁定能力(底層其實優(yōu)化了很多),原先的ng-model指令升級成了ngModel,使用的功能保持不變。
同時盡管ng2版本的數(shù)據(jù)雙向綁定得到了很大的優(yōu)化,仍改變不了其數(shù)據(jù)異步綁定的方式,因為ng2不能確定數(shù)據(jù)何時綁定,我們也不能確定很多網(wǎng)絡(luò)請求得到的數(shù)據(jù)到來的時間。
在ng1中其實這個機制會有一些尷尬的場景,至少筆者在一些情況下不得不在一些業(yè)務(wù)場景下使用setTimeout來保證數(shù)據(jù)已經(jīng)成功綁定進入scope的watch循環(huán),但這個異步綁定數(shù)據(jù)又是不可避免的,除非我們自己來適應(yīng)實際項目改寫angular代碼了。
所以ng2就提供了讓我們配合具體項目場景改寫ngModel的能力,也就是原文介紹的響應(yīng)式表單。
其跟ngModel的關(guān)系就是,ngModel是響應(yīng)式表單的官方實現(xiàn),其在我們綁定數(shù)據(jù)時自動為我們實現(xiàn)響應(yīng)式表單中用到的幾個機制,如果我們需要數(shù)據(jù)嚴格的實時同步綁定,就不必使用ngModel,可以親自來編寫響應(yīng)式的表單,步驟覆蓋了組件模板到數(shù)據(jù)模型類整條龍,而這么多事情在合適的場景下使用ngModel已經(jīng)可以實現(xiàn)了,這兩種表單綁定的方式各有其優(yōu)勢。
1. 使用響應(yīng)式表單
響應(yīng)式表單的能力封裝在ReactiveFormsModule中,并且跟FormsModule同時包含在@angular/forms這個包中。
表單類的要點:
1.AbstractControl是FormControl、FormGroup、FormArray這三個實例表單類的抽象基類。它提供了他們的通用行為以及屬性,其中就有observable。
2.FormControl在單個表單控件中檢查值并驗證狀態(tài)。它負責將其通知給HTML表單控件(比如input這些)。
3.FormGroup負責AbstractControl實例的一個組的值與驗證狀態(tài)。組的屬性包含了它們的子控件。你的組件的頂級表單就是一個FormGroup。
4.FormArray負責AbstractControl實例的數(shù)值索引數(shù)組的值與狀態(tài)驗證。
2. FormControl
最核心的指令就是FormControl,算是底層的ngModel,在模板標簽中跟定義好的數(shù)據(jù)模型字段綁定起來,就像這樣:
<h2>Hero Detail</h2> <h3><i>Just a FormControl</i></h3> <label class="center-block">Name: <input class="form-control" [formControl]="name"> </label>
同時在組件代碼中需要將上例中這個name字段聲明為FormControl類:
export class HeroDetailComponent1 { name = new FormControl(); }
3. FormGroup
將多個FormControl對象分組到FormGroup中,用來方便管理。定義方法如下:
import { Component } from '@angular/core'; import { FormControl, FormGroup } from '@angular/forms'; export class HeroDetailComponent2 { heroForm = new FormGroup ({ name: new FormControl() }); }
此時模板標簽中也要分組來寫:
<h2>Hero Detail</h2> <h3><i>FormControl in a FormGroup</i></h3> <form [formGroup]="heroForm" novalidate> <div class="form-group"> <label class="center-block">Name: <input class="form-control" formControlName="name"> </label> </div> </form>
現(xiàn)在的效果就是可以從heroForm中實時讀取到其值和一些附加的狀態(tài)的變化。
可以在模板中添加兩個標簽來展示數(shù)據(jù)的更改:
<p>Form value: {{ heroForm.value | json }}</p> <p>Form status: {{ heroForm.status | json }}</p>
4. FormBuilder
還有個新概念就是FormBuilder,是用來幫助創(chuàng)建表單類的:
1. 顯示聲明heroForm屬性的類型為FormGroup,后面你將會初始化它
2. 注入FormBuilder到構(gòu)造器中
3. 使用FormsBuilder添加新方法定義heroForm,叫做createForm。
4. 在構(gòu)造器中執(zhí)行createForm方法。
export class HeroDetailComponent3 { heroForm: FormGroup; // <--- heroForm is of type FormGroup constructor(private fb: FormBuilder) { // <--- inject FormBuilder this.createForm(); } createForm() { this.heroForm = this.fb.group({ name: '', // <--- the FormControl called "name" }); } }
上例中執(zhí)行createForm方法即可動態(tài)快速的創(chuàng)建出表單類,其在一些表單類需要更改的場景下可以使用。
5. setValue和patchValue
這兩個方法是真正給表單模型賦值用的。因為表單顯示的數(shù)據(jù)與真實的底層數(shù)據(jù)肯定不能使同一個,否則表單輸入數(shù)據(jù)一旦更改,源數(shù)據(jù)就被污染了,而這兩個方法就是用來將源數(shù)據(jù)賦值到表單模型數(shù)據(jù)上的。
每當需要賦值時就可以調(diào)用,其中setValue必須準確賦值,并且會在數(shù)據(jù)不匹配時報告錯誤;而patchValue沒有這么嚴格,但可以傳一個對象,且不匹配時不會報告錯誤。
而我們要做的就是在ng2組件的ngOnChanges回調(diào)中手動執(zhí)行setValue設(shè)置數(shù)據(jù)值。比如這樣:
ngOnChanges() this.heroForm.setValue({ name: this.hero.name, address: this.hero.addresses[0] || new Address() }); }
同時ng2還提供了一個reset方法來重新調(diào)用setValue方法(setValue本身好像只是用來一次性賦值的)。
6. FormArray
FormArray是用來對付FormGroups列表的,比如一個英雄有可能有多個address字段,address字段本身就是個FormGroup,此時就要用到FormArray了:
this.heroForm = this.fb.group({ name: ['', Validators.required ], secretLairs: this.fb.array([]), // <-- secretLairs as an empty FormArray power: '', sidekick: '' });
獲取FormArray要用到FormGroup提供的一個get方法:
get secretLairs(): FormArray { return this.heroForm.get('secretLairs') as FormArray; };
其顯示的模板如下:
<div formArrayName="secretLairs" class="well well-lg"> <div *ngFor="let address of secretLairs.controls; let i=index" [formGroupName]="i" > <!-- The repeated address template --> </div> </div>
效果就是定義好這個FormArray以后就可以使用ngFor把這一個表單列表遍歷出來了(直接使用ngModel加ngFor能省不少事情...)。
總結(jié):
筆者目前使用ng2還沒涉及到比較復雜的表單,所以對原文提到的內(nèi)容理解并不是很深,加上老外寫的文章通常都過于完整,都喜歡用長篇大論來說明一個簡單的知識點,所以本文后半段其實沒多少原文的影子,純屬筆者自己拙劣的概括,并且沒有做過太多實踐。
回看ng2的響應(yīng)式表單能力,提供的指令以及服務(wù)也就這么幾個(FormControl、FormGroup、FormArray、FormBuilder以及幾個功能性方法),巧妙在使用這些能力就能完成一個強大的表單界面,其編碼體驗絕對是遠超傳統(tǒng)jQuery強行從DOM讀取節(jié)點值的方式的,并且提供了除了簡單的ngModel能力之外的更具體更強大的數(shù)據(jù)綁定方案,還有本文未提及的表單驗證這個大內(nèi)容,在ng2提供的這個響應(yīng)式表單方案下實現(xiàn)起來也是很得心應(yīng)手的。
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Ionic+AngularJS實現(xiàn)登錄和注冊帶驗證功能
這篇文章主要介紹了Ionic+AngularJS實現(xiàn)登錄和注冊帶驗證功能,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-02-02Angular.js如何從PHP讀取后臺數(shù)據(jù)
這篇文章主要為大家簡單介紹了Angular.js如何從PHP讀取后臺數(shù)據(jù),本文將Angular和PHP相結(jié)合,從后臺讀取數(shù)據(jù),感興趣的小伙伴們可以參考一下2016-03-03AngularJS select設(shè)置默認值的實現(xiàn)方法
這篇文章主要介紹了AngularJS select設(shè)置默認值的實現(xiàn)方法的相關(guān)資料,這里提供實現(xiàn)方法幫助大家實現(xiàn)這樣的功能,需要的朋友可以參考下2017-08-08