Angular 表單-介紹

2022-07-05 13:57 更新

Angular 表單簡(jiǎn)介

用表單處理用戶(hù)輸入是許多常見(jiàn)應(yīng)用的基礎(chǔ)功能。 應(yīng)用通過(guò)表單來(lái)讓用戶(hù)登錄、修改個(gè)人檔案、輸入敏感信息以及執(zhí)行各種數(shù)據(jù)輸入任務(wù)。

Angular 提供了兩種不同的方法來(lái)通過(guò)表單處理用戶(hù)輸入:響應(yīng)式表單和模板驅(qū)動(dòng)表單。 兩者都從視圖中捕獲用戶(hù)輸入事件、驗(yàn)證用戶(hù)輸入、創(chuàng)建表單模型、修改數(shù)據(jù)模型,并提供跟蹤這些更改的途徑。

本指南提供的信息可以幫你確定哪種方式最適合你的情況。它介紹了這兩種方法所用的公共構(gòu)造塊,還總結(jié)了兩種方式之間的關(guān)鍵區(qū)別,并在建立、數(shù)據(jù)流和測(cè)試等不同的情境下展示了這些差異。

選擇一種方法

響應(yīng)式表單和模板驅(qū)動(dòng)表單以不同的方式處理和管理表單數(shù)據(jù)。每種方法都有各自的優(yōu)點(diǎn)。

表單

詳情

響應(yīng)式表單

提供對(duì)底層表單對(duì)象模型直接、顯式的訪(fǎng)問(wèn)。它們與模板驅(qū)動(dòng)表單相比,更加健壯:它們的可擴(kuò)展性、可復(fù)用性和可測(cè)試性都更高。如果表單是你的應(yīng)用程序的關(guān)鍵部分,或者你已經(jīng)在使用響應(yīng)式表單來(lái)構(gòu)建應(yīng)用,那就使用響應(yīng)式表單。

模板驅(qū)動(dòng)表單

依賴(lài)模板中的指令來(lái)創(chuàng)建和操作底層的對(duì)象模型。它們對(duì)于向應(yīng)用添加一個(gè)簡(jiǎn)單的表單非常有用,比如電子郵件列表注冊(cè)表單。它們很容易添加到應(yīng)用中,但在擴(kuò)展性方面不如響應(yīng)式表單。如果你有可以只在模板中管理的非常基本的表單需求和邏輯,那么模板驅(qū)動(dòng)表單就很合適。

關(guān)鍵差異

下表總結(jié)了響應(yīng)式表單和模板驅(qū)動(dòng)表單之間的一些關(guān)鍵差異。

響應(yīng)式

模板驅(qū)動(dòng)

建立表單模型

顯式的,在組件類(lèi)中創(chuàng)建

隱式的,由指令創(chuàng)建

數(shù)據(jù)模型

結(jié)構(gòu)化和不可變的

非結(jié)構(gòu)化和可變的

數(shù)據(jù)流

同步

異步

表單驗(yàn)證

函數(shù)

指令

可伸縮性

如果表單是應(yīng)用程序的核心部分,那么可伸縮性就非常重要。能夠跨組件復(fù)用表單模型是至關(guān)重要的。

響應(yīng)式表單比模板驅(qū)動(dòng)表單更有可伸縮性。它們提供對(duì)底層表單 API 的直接訪(fǎng)問(wèn),并且在視圖和數(shù)據(jù)模型之間使用同步數(shù)據(jù)流,從而可以更輕松地創(chuàng)建大型表單。響應(yīng)式表單需要較少的測(cè)試設(shè)置,測(cè)試時(shí)不需要深入理解變更檢測(cè),就能正確測(cè)試表單更新和驗(yàn)證。

模板驅(qū)動(dòng)表單專(zhuān)注于簡(jiǎn)單的場(chǎng)景,可復(fù)用性沒(méi)那么高。它們抽象出了底層表單 API,并且在視圖和數(shù)據(jù)模型之間使用異步數(shù)據(jù)流。對(duì)模板驅(qū)動(dòng)表單的這種抽象也會(huì)影響測(cè)試。測(cè)試程序非常依賴(lài)于手動(dòng)觸發(fā)變更檢測(cè)才能正常運(yùn)行,并且需要進(jìn)行更多設(shè)置工作。

建立表單模型

響應(yīng)式表單和模板驅(qū)動(dòng)型表單都會(huì)跟蹤用戶(hù)與之交互的表單輸入元素和組件模型中的表單數(shù)據(jù)之間的值變更。這兩種方法共享同一套底層構(gòu)建塊,只在如何創(chuàng)建和管理常用表單控件實(shí)例方面有所不同。

常用表單基礎(chǔ)類(lèi)

響應(yīng)式表單和模板驅(qū)動(dòng)表單都建立在下列基礎(chǔ)類(lèi)之上。

基類(lèi)

詳情

FormControl

追蹤單個(gè)表單控件的值和驗(yàn)證狀態(tài)。

FormGroup

追蹤一個(gè)表單控件組的值和狀態(tài)。

FormArray

追蹤表單控件數(shù)組的值和狀態(tài)。

ControlValueAccessor

在 Angular 的 FormControl 實(shí)例和內(nèi)置 DOM 元素之間創(chuàng)建一個(gè)橋梁

建立響應(yīng)式表單

對(duì)于響應(yīng)式表單,你可以直接在組件類(lèi)中定義表單模型。?[formControl]? 指令會(huì)通過(guò)內(nèi)部值訪(fǎng)問(wèn)器來(lái)把顯式創(chuàng)建的 ?FormControl ?實(shí)例與視圖中的特定表單元素聯(lián)系起來(lái)。

下面的組件使用響應(yīng)式表單為單個(gè)控件實(shí)現(xiàn)了一個(gè)輸入字段。在這個(gè)例子中,表單模型是 ?FormControl ?實(shí)例。

import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';

@Component({
  selector: 'app-reactive-favorite-color',
  template: `
    Favorite Color: <input type="text" [formControl]="favoriteColorControl">
  `
})
export class FavoriteColorComponent {
  favoriteColorControl = new FormControl('');
}

圖 1 展示了在響應(yīng)式表單中,表單模型是如何成為事實(shí)之源(source of truth)的。它通過(guò)輸入元素上的 ?[formControl]? 指令,在任何給定的時(shí)間點(diǎn)提供表單元素的值和狀態(tài)。


圖 1. 在響應(yīng)式表單中直接訪(fǎng)問(wèn)表單模型

建立模板驅(qū)動(dòng)表單

在模板驅(qū)動(dòng)表單中,表單模型是隱式的,而不是顯式的。指令 ?NgModel ?為指定的表單元素創(chuàng)建并管理一個(gè) ?FormControl ?實(shí)例。

下面的組件使用模板驅(qū)動(dòng)表單為單個(gè)控件實(shí)現(xiàn)了同樣的輸入字段。

import { Component } from '@angular/core';

@Component({
  selector: 'app-template-favorite-color',
  template: `
    Favorite Color: <input type="text" [(ngModel)]="favoriteColor">
  `
})
export class FavoriteColorComponent {
  favoriteColor = '';
}

在模板驅(qū)動(dòng)表單中,其事實(shí)之源就是模板。你沒(méi)有對(duì) ?FormControl ?實(shí)例的直接編程訪(fǎng)問(wèn),如圖 2 所示。


圖 2. 模板驅(qū)動(dòng)表單中對(duì)表單模型的間接訪(fǎng)問(wèn)

表單中的數(shù)據(jù)流

當(dāng)應(yīng)用包含一個(gè)表單時(shí),Angular 必須讓該視圖與組件模型保持同步,并讓組件模型與視圖保持同步。當(dāng)用戶(hù)通過(guò)視圖更改值并進(jìn)行選擇時(shí),新值必須反映在數(shù)據(jù)模型中。同樣,當(dāng)程序邏輯改變數(shù)據(jù)模型中的值時(shí),這些值也必須反映到視圖中。

響應(yīng)式表單和模板驅(qū)動(dòng)表單在處理來(lái)自用戶(hù)或程序化變更時(shí)的數(shù)據(jù)處理方式上有所不同。下面的這些原理圖會(huì)以上面定義的 ?favorite-color? 輸入字段為例,分別說(shuō)明兩種表單各自的數(shù)據(jù)流。

響應(yīng)式表單中的數(shù)據(jù)流

在響應(yīng)式表單中,視圖中的每個(gè)表單元素都直接鏈接到一個(gè)表單模型(?FormControl ?實(shí)例)。 從視圖到模型的修改以及從模型到視圖的修改都是同步的,而且不依賴(lài)于 UI 的渲染方式。

這個(gè)視圖到模型的圖表展示了當(dāng)輸入字段的值發(fā)生變化時(shí),數(shù)據(jù)流是如何從視圖開(kāi)始經(jīng)過(guò)下列步驟進(jìn)行流動(dòng)的。

  1. 最終用戶(hù)在輸入框元素中鍵入了一個(gè)值,這里是 "Blue"。
  2. 這個(gè)輸入框元素會(huì)發(fā)出一個(gè)帶有最新值的 "input" 事件。
  3. 這個(gè)控件值訪(fǎng)問(wèn)器 ?ControlValueAccessor ?會(huì)監(jiān)聽(tīng)表單輸入框元素上的事件,并立即把新值傳給 ?FormControl ?實(shí)例。
  4. ?FormControl ?實(shí)例會(huì)通過(guò) ?valueChanges ?這個(gè)可觀察對(duì)象發(fā)出這個(gè)新值。
  5. ?valueChanges ?的任何一個(gè)訂閱者都會(huì)收到這個(gè)新值。


這個(gè)模型到視圖的示意圖體現(xiàn)了程序中對(duì)模型的修改是如何通過(guò)下列步驟傳播到視圖中的。

  1. ?favoriteColorControl.setValue()? 方法被調(diào)用,它會(huì)更新這個(gè) ?FormControl ?的值。
  2. ?FormControl ?實(shí)例會(huì)通過(guò) ?valueChanges ?這個(gè)可觀察對(duì)象發(fā)出新值。
  3. ?valueChanges ?的任何訂閱者都會(huì)收到這個(gè)新值。
  4. 該表單輸入框元素上的控件值訪(fǎng)問(wèn)器會(huì)把控件更新為這個(gè)新值。


模板驅(qū)動(dòng)表單中的數(shù)據(jù)流

在模板驅(qū)動(dòng)表單中,每一個(gè)表單元素都是和一個(gè)負(fù)責(zé)管理內(nèi)部表單模型的指令關(guān)聯(lián)起來(lái)的。

這個(gè)視圖到模型的圖表展示了當(dāng)輸入字段的值發(fā)生變化時(shí),數(shù)據(jù)流是如何從視圖開(kāi)始經(jīng)過(guò)下列步驟進(jìn)行流動(dòng)的。

  1. 最終用戶(hù)在輸入框元素中敲 "Blue"。
  2. 該輸入框元素會(huì)發(fā)出一個(gè) "input" 事件,帶著值 "Blue"。
  3. 附著在該輸入框上的控件值訪(fǎng)問(wèn)器會(huì)觸發(fā) ?FormControl ?實(shí)例上的 ?setValue()? 方法。
  4. ?FormControl ?實(shí)例通過(guò) ?valueChanges ?這個(gè)可觀察對(duì)象發(fā)出新值。
  5. ?valueChanges ?的任何訂閱者都會(huì)收到新值。
  6. 控件值訪(fǎng)問(wèn)器 ?ControlValueAccessory ?還會(huì)調(diào)用 ?NgModel.viewToModelUpdate()? 方法,它會(huì)發(fā)出一個(gè) ?ngModelChange ?事件。
  7. 由于該組件模板雙向數(shù)據(jù)綁定到了 ?favoriteColor?,組件中的 ?favoriteColor ?屬性就會(huì)修改為 ?ngModelChange ?事件所發(fā)出的值("Blue")。


這個(gè)模型到視圖的示意圖展示了當(dāng) ?favoriteColor ?從藍(lán)變到紅時(shí),數(shù)據(jù)是如何經(jīng)過(guò)如下步驟從模型流動(dòng)到視圖的。

  1. 組件中修改了 ?favoriteColor ?的值。
  2. 變更檢測(cè)開(kāi)始。
  3. 在變更檢測(cè)期間,由于這些輸入框之一的值發(fā)生了變化,Angular 就會(huì)調(diào)用 ?NgModel ?指令上的 ?ngOnChanges ?生命周期鉤子。
  4. ?ngOnChanges()? 方法會(huì)把一個(gè)異步任務(wù)排入隊(duì)列,以設(shè)置內(nèi)部 ?FormControl ?實(shí)例的值。
  5. 變更檢測(cè)完成。
  6. 在下一個(gè)檢測(cè)周期,用來(lái)為 ?FormControl ?實(shí)例賦值的任務(wù)就會(huì)執(zhí)行。
  7. ?FormControl ?實(shí)例通過(guò)可觀察對(duì)象 ?valueChanges ?發(fā)出最新值。
  8. ?valueChanges ?的任何訂閱者都會(huì)收到這個(gè)新值。
  9. 控件值訪(fǎng)問(wèn)器 ?ControlValueAccessor ?會(huì)使用 ?favoriteColor ?的最新值來(lái)修改表單的輸入框元素。


數(shù)據(jù)模型的可變性

變更追蹤的方法對(duì)應(yīng)用的效率有著重要影響。

表格

詳細(xì)信息

響應(yīng)式表單

通過(guò)以不可變的數(shù)據(jù)結(jié)構(gòu)提供數(shù)據(jù)模型,來(lái)保持?jǐn)?shù)據(jù)模型的純粹性。每當(dāng)在數(shù)據(jù)模型上觸發(fā)更改時(shí),FormControl 實(shí)例都會(huì)返回一個(gè)新的數(shù)據(jù)模型,而不會(huì)更新現(xiàn)有的數(shù)據(jù)模型。這使你能夠通過(guò)該控件的可觀察對(duì)象跟蹤對(duì)數(shù)據(jù)模型的唯一更改。這讓變更檢測(cè)更有效率,因?yàn)樗恍柙谖ㄒ恍愿模ㄗg注:也就是對(duì)象引用發(fā)生變化)時(shí)進(jìn)行更新。由于數(shù)據(jù)更新遵循響應(yīng)式模式,因此你可以把它和可觀察對(duì)象的各種運(yùn)算符集成起來(lái)以轉(zhuǎn)換數(shù)據(jù)。

模板驅(qū)動(dòng)表單

依賴(lài)于可變性和雙向數(shù)據(jù)綁定,可以在模板中做出更改時(shí)更新組件中的數(shù)據(jù)模型。由于使用雙向數(shù)據(jù)綁定時(shí)沒(méi)有用來(lái)對(duì)數(shù)據(jù)模型進(jìn)行跟蹤的唯一性更改,因此變更檢測(cè)在需要確定何時(shí)更新時(shí)效率較低。

前面那些使用 ?favorite-color? 輸入元素的例子就演示了這種差異。

  • 對(duì)于響應(yīng)式表單,當(dāng)控件值更新時(shí),?FormControl ?的實(shí)例總會(huì)返回一個(gè)新值
  • 對(duì)于模板驅(qū)動(dòng)表單,?favorite-color? 屬性總會(huì)被修改為新值

表單驗(yàn)證

驗(yàn)證是管理任何表單時(shí)必備的一部分。無(wú)論你是要檢查必填項(xiàng),還是查詢(xún)外部 API 來(lái)檢查用戶(hù)名是否已存在,Angular 都會(huì)提供一組內(nèi)置的驗(yàn)證器,以及創(chuàng)建自定義驗(yàn)證器所需的能力。

表格

詳細(xì)信息

響應(yīng)式表單

把自定義驗(yàn)證器定義成函數(shù),它以要驗(yàn)證的控件作為參數(shù)

模板驅(qū)動(dòng)表單

和模板指令緊密相關(guān),并且必須提供包裝了驗(yàn)證函數(shù)的自定義驗(yàn)證器指令

測(cè)試

測(cè)試在復(fù)雜的應(yīng)用程序中也起著重要的作用。當(dāng)驗(yàn)證你的表單功能是否正確時(shí),更簡(jiǎn)單的測(cè)試策略往往也更有用。測(cè)試響應(yīng)式表單和模板驅(qū)動(dòng)表單的差別之一在于它們是否需要渲染 UI 才能基于表單控件和表單字段變化來(lái)執(zhí)行斷言。下面的例子演示了使用響應(yīng)式表單和模板驅(qū)動(dòng)表單時(shí)表單的測(cè)試過(guò)程。

測(cè)試響應(yīng)式表單

響應(yīng)式表單提供了相對(duì)簡(jiǎn)單的測(cè)試策略,因?yàn)樗鼈兡芴峁?duì)表單和數(shù)據(jù)模型的同步訪(fǎng)問(wèn),而且不必渲染 UI 就能測(cè)試它們。在這些測(cè)試中,控件和數(shù)據(jù)是通過(guò)控件進(jìn)行查詢(xún)和操縱的,不需要和變更檢測(cè)周期打交道。

下面的測(cè)試?yán)们懊胬又械?nbsp;"喜歡的顏色" 組件來(lái)驗(yàn)證響應(yīng)式表單中的 "從視圖到模型" 和 "從模型到視圖" 數(shù)據(jù)流。

驗(yàn)證“從視圖到模型”的數(shù)據(jù)流

第一個(gè)例子執(zhí)行了下列步驟來(lái)驗(yàn)證“從視圖到模型”數(shù)據(jù)流。

  1. 查詢(xún)表單輸入框元素的視圖,并為測(cè)試創(chuàng)建自定義的 "input" 事件
  2. 把輸入的新值設(shè)置為 Red,并在表單輸入元素上調(diào)度 "input" 事件。
  3. 斷言該組件的 ?favoriteColorControl ?的值與來(lái)自輸入框的值是匹配的。
  4. it('should update the value of the input field', () => {
      const input = fixture.nativeElement.querySelector('input');
      const event = createNewEvent('input');
    
      input.value = 'Red';
      input.dispatchEvent(event);
    
      expect(fixture.componentInstance.favoriteColorControl.value).toEqual('Red');
    });

下一個(gè)例子執(zhí)行了下列步驟來(lái)驗(yàn)證“從模型到視圖”數(shù)據(jù)流。

  1. 使用 ?favoriteColorControl ?這個(gè) ?FormControl ?實(shí)例來(lái)設(shè)置新值。
  2. 查詢(xún)表單中輸入框的視圖。
  3. 斷言控件上設(shè)置的新值與輸入中的值是匹配的。
  4. it('should update the value in the control', () => {
      component.favoriteColorControl.setValue('Blue');
    
      const input = fixture.nativeElement.querySelector('input');
    
      expect(input.value).toBe('Blue');
    });

測(cè)試模板驅(qū)動(dòng)表單

使用模板驅(qū)動(dòng)表單編寫(xiě)測(cè)試就需要詳細(xì)了解變更檢測(cè)過(guò)程,以及指令在每個(gè)變更檢測(cè)周期中如何運(yùn)行,以確保在正確的時(shí)間查詢(xún)、測(cè)試或更改元素。

下面的測(cè)試使用了以前的 "喜歡的顏色" 組件,來(lái)驗(yàn)證模板驅(qū)動(dòng)表單的 "從視圖到模型" 和 "從模型到視圖" 數(shù)據(jù)流。

下面的測(cè)試驗(yàn)證了 "從視圖到模型" 數(shù)據(jù)流:

it('should update the favorite color in the component', fakeAsync(() => {
     const input = fixture.nativeElement.querySelector('input');
     const event = createNewEvent('input');

     input.value = 'Red';
     input.dispatchEvent(event);

     fixture.detectChanges();

     expect(component.favoriteColor).toEqual('Red');
   }));

這個(gè) "視圖到模型" 測(cè)試的執(zhí)行步驟如下:

  1. 查詢(xún)表單輸入元素中的視圖,并為測(cè)試創(chuàng)建自定義 "input" 事件。
  2. 把輸入框的新值設(shè)置為 Red,并在表單輸入框元素上派發(fā) "input" 事件。
  3. 通過(guò)測(cè)試夾具(Fixture)來(lái)運(yùn)行變更檢測(cè)。
  4. 斷言該組件 ?favoriteColor ?屬性的值與來(lái)自輸入框的值是匹配的。

下面的測(cè)試驗(yàn)證了 "從模型到視圖" 的數(shù)據(jù)流:

it('should update the favorite color on the input field', fakeAsync(() => {
     component.favoriteColor = 'Blue';

     fixture.detectChanges();

     tick();

     const input = fixture.nativeElement.querySelector('input');

     expect(input.value).toBe('Blue');
   }));

這個(gè) "模型到視圖" 測(cè)試的執(zhí)行步驟如下:

  1. 使用組件實(shí)例來(lái)設(shè)置 ?favoriteColor ?的值。
  2. 通過(guò)測(cè)試夾具(Fixture)來(lái)運(yùn)行變更檢測(cè)。
  3. 在 ?fakeAsync()? 任務(wù)中使用 ?tick()? 方法來(lái)模擬時(shí)間的流逝。
  4. 查詢(xún)表單輸入框元素的視圖。
  5. 斷言輸入框的值與該組件實(shí)例的 ?favoriteColor ?屬性值是匹配的。


以上內(nèi)容是否對(duì)您有幫助:
在線(xiàn)筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)