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