Angular9 響應(yīng)式表單

2020-07-01 16:53 更新

響應(yīng)式表單提供了一種模型驅(qū)動的方式來處理表單輸入,其中的值會隨時間而變化。本文會向你展示如何創(chuàng)建和更新基本的表單控件,接下來還會在一個表單組中使用多個控件,驗(yàn)證表單的值,以及創(chuàng)建動態(tài)表單,也就是在運(yùn)行期添加或移除控件。

先決條件

在深入了解被動表單之前,你應(yīng)該對這些內(nèi)容有一個基本的了解:

  • TypeScript 編程。

  • Angular 的應(yīng)用設(shè)計基礎(chǔ),就像Angular Concepts 中描述的那樣。

  • “表單簡介”中提供的表單設(shè)計概念。

響應(yīng)式表單概述

響應(yīng)式表單使用顯式的、不可變的方式,管理表單在特定的時間點(diǎn)上的狀態(tài)。對表單狀態(tài)的每一次變更都會返回一個新的狀態(tài),這樣可以在變化時維護(hù)模型的整體性。響應(yīng)式表單是圍繞 Observable 流構(gòu)建的,表單的輸入和值都是通過這些輸入值組成的流來提供的,它可以同步訪問。

響應(yīng)式表單還提供了一種更直觀的測試路徑,因?yàn)樵谡埱髸r你可以確信這些數(shù)據(jù)是一致的、可預(yù)料的。這個流的任何一個消費(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ù)來異步跟蹤變化。參見表單概覽以了解這兩種范式之間的詳細(xì)比較。

添加基礎(chǔ)表單控件

使用表單控件有三個步驟。

  1. 在你的應(yīng)用中注冊響應(yīng)式表單模塊。該模塊聲明了一些你要用在響應(yīng)式表單中的指令。

  1. 生成一個新的 FormControl 實(shí)例,并把它保存在組件中。

  1. 在模板中注冊這個 FormControl

然后,你可以把組件添加到模板中來顯示表單。

下面的例子展示了如何添加一個表單控件。在這個例子中,用戶在輸入字段中輸入自己的名字,捕獲其輸入值,并顯示表單控件的當(dāng)前值。

注冊響應(yīng)式表單模塊

要使用響應(yīng)式表單控件,就要從 @angular/forms 包中導(dǎo)入 ReactiveFormsModule,并把它添加到你的 NgModuleimports 數(shù)組中。

Path:"src/app/app.module.ts (excerpt)" 。

import { ReactiveFormsModule } from '@angular/forms';


@NgModule({
  imports: [
    // other imports ...
    ReactiveFormsModule
  ],
})
export class AppModule { }

生成一個新的 FormControl

使用 CLI 命令 ng generate 在項(xiàng)目中生成一個組件作為該表單控件的宿主。

ng generate component NameEditor

要注冊一個表單控件,就要導(dǎo)入 FormControl 類并創(chuàng)建一個 FormControl 的新實(shí)例,將其保存為類的屬性。

Path:"src/app/name-editor/name-editor.component.ts" 。

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


@Component({
  selector: 'app-name-editor',
  templateUrl: './name-editor.component.html',
  styleUrls: ['./name-editor.component.css']
})
export class NameEditorComponent {
  name = new FormControl('');
}

可以用 FormControl 的構(gòu)造函數(shù)設(shè)置初始值,這個例子中它是空字符串。通過在你的組件類中創(chuàng)建這些控件,你可以直接對表單控件的狀態(tài)進(jìn)行監(jiān)聽、修改和校驗(yàn)。

在模板中注冊該控件

在組件類中創(chuàng)建了控件之后,你還要把它和模板中的一個表單控件關(guān)聯(lián)起來。修改模板,為表單控件添加 formControl 綁定,formControl 是由 ReactiveFormsModule 中的 FormControlDirective 提供的。

Path:"src/app/name-editor/name-editor.component.html" 。

<label>
  Name:
  <input type="text" [formControl]="name">
</label>

使用這種模板綁定語法,把該表單控件注冊給了模板中名為 name 的輸入元素。這樣,表單控件和 DOM 元素就可以互相通訊了:視圖會反映模型的變化,模型也會反映視圖中的變化。

顯示該組件

把該組件添加到模板時,將顯示指派給 name 的表單控件。

Path:"src/app/app.component.html (name editor)" 。

<app-name-editor></app-name-editor>

顯示表單控件的值

你可以用下列方式顯示它的值:

  • 通過可觀察對象 valueChanges,你可以在模板中使用 AsyncPipe 或在組件類中使用 subscribe() 方法來監(jiān)聽表單值的變化。

  • 使用 value 屬性。它能讓你獲得當(dāng)前值的一份快照。

下面的例子展示了如何在模板中使用插值顯示當(dāng)前值。

Path:"src/app/name-editor/name-editor.component.html (control value)" 。

<p>
  Value: {{ name.value }}
</p>

一旦你修改了表單控件所關(guān)聯(lián)的元素,這里顯示的值也跟著變化了。

響應(yīng)式表單還能通過每個實(shí)例的屬性和方法提供關(guān)于特定控件的更多信息。AbstractControl 的這些屬性和方法用于控制表單狀態(tài),并在處理表單校驗(yàn)時決定何時顯示信息。

替換表單控件的值

響應(yīng)式表單還有一些方法可以用編程的方式修改控件的值,它讓你可以靈活的修改控件的值而不需要借助用戶交互。FormControl 提供了一個 setValue() 方法,它會修改這個表單控件的值,并且驗(yàn)證與控件結(jié)構(gòu)相對應(yīng)的值的結(jié)構(gòu)。比如,當(dāng)從后端 API 或服務(wù)接收到了表單數(shù)據(jù)時,可以通過 setValue() 方法來把原來的值替換為新的值。

下列的例子往組件類中添加了一個方法,它使用 setValue() 方法來修改 Nancy 控件的值。

Path:"src/app/name-editor/name-editor.component.ts (update value)" 。

updateName() {
  this.name.setValue('Nancy');
}

修改模板,添加一個按鈕,用于模擬改名操作。在點(diǎn) Update Name 按鈕之前表單控件元素中輸入的任何值都會回顯為它的當(dāng)前值。

Path:"src/app/name-editor/name-editor.component.html (update value)" 。

<p>
  <button (click)="updateName()">Update Name</button>
</p>

由于表單模型是該控件的事實(shí)之源,因此當(dāng)你單擊該按鈕時,組件中該輸入框的值也變化了,覆蓋掉它的當(dāng)前值。

注:
- 在這個例子中,你只使用單個控件,但是當(dāng)調(diào)用 FormGroupFormArray 實(shí)例的 setValue() 方法時,傳入的值就必須匹配控件組或控件數(shù)組的結(jié)構(gòu)才行。

把表單控件分組

表單中通常會包含幾個相互關(guān)聯(lián)的控件。響應(yīng)式表單提供了兩種把多個相關(guān)控件分組到同一個輸入表單中的方法。

  • 表單組定義了一個帶有一組控件的表單,你可以把它們放在一起管理。表單組的基礎(chǔ)知識將在本節(jié)中討論。你也可以通過嵌套表單組來創(chuàng)建更復(fù)雜的表單。

  • 表單數(shù)組定義了一個動態(tài)表單,你可以在運(yùn)行時添加和刪除控件。你也可以通過嵌套表單數(shù)組來創(chuàng)建更復(fù)雜的表單。欲知詳情,參見下面的創(chuàng)建動態(tài)表單。

就像 FormControl 的實(shí)例能讓你控制單個輸入框所對應(yīng)的控件一樣,FormGroup 的實(shí)例也能跟蹤一組 FormControl 實(shí)例(比如一個表單)的表單狀態(tài)。當(dāng)創(chuàng)建 FormGroup 時,其中的每個控件都會根據(jù)其名字進(jìn)行跟蹤。下面的例子展示了如何管理單個控件組中的多個 FormControl 實(shí)例。

生成一個 ProfileEditor 組件并從 @angular/forms 包中導(dǎo)入 FormGroupFormControl 類。

ng generate component ProfileEditor

Path:"src/app/profile-editor/profile-editor.component.ts (imports)" 。

import { FormGroup, FormControl } from '@angular/forms';

要將表單組添加到此組件中,請執(zhí)行以下步驟。

  1. 創(chuàng)建一個 FormGroup 實(shí)例。

  1. 把這個 FormGroup 模型關(guān)聯(lián)到視圖。

  1. 保存表單數(shù)據(jù)。

創(chuàng)建一個 FormGroup 實(shí)例

在組件類中創(chuàng)建一個名叫 profileForm 的屬性,并設(shè)置為 FormGroup 的一個新實(shí)例。要初始化這個 FormGroup,請為構(gòu)造函數(shù)提供一個由控件組成的對象,對象中的每個名字都要和表單控件的名字一一對應(yīng)。

對此個人檔案表單,要添加兩個 FormControl 實(shí)例,名字分別為 firstNamelastName。

Path:"src/app/profile-editor/profile-editor.component.ts (form group)" 。

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


@Component({
  selector: 'app-profile-editor',
  templateUrl: './profile-editor.component.html',
  styleUrls: ['./profile-editor.component.css']
})
export class ProfileEditorComponent {
  profileForm = new FormGroup({
    firstName: new FormControl(''),
    lastName: new FormControl(''),
  });
}

現(xiàn)在,這些獨(dú)立的表單控件被收集到了一個控件組中。這個 FormGroup 用對象的形式提供了它的模型值,這個值來自組中每個控件的值。 FormGroup 實(shí)例擁有和 FormControl 實(shí)例相同的屬性(比如 value、untouched)和方法(比如 setValue())。

把這個 FormGroup 模型關(guān)聯(lián)到視圖。

這個表單組還能跟蹤其中每個控件的狀態(tài)及其變化,所以如果其中的某個控件的狀態(tài)或值變化了,父控件也會發(fā)出一次新的狀態(tài)變更或值變更事件。該控件組的模型來自它的所有成員。在定義了這個模型之后,你必須更新模板,來把該模型反映到視圖中。

Path:"src/app/profile-editor/profile-editor.component.html (template form group)" 。

<form [formGroup]="profileForm">

  
  <label>
    First Name:
    <input type="text" formControlName="firstName">
  </label>


  <label>
    Last Name:
    <input type="text" formControlName="lastName">
  </label>


</form>

注意,就像 FormGroup 所包含的那控件一樣,profileForm 這個 FormGroup 也通過 FormGroup 指令綁定到了 form 元素,在該模型和表單中的輸入框之間創(chuàng)建了一個通訊層。 由 FormControlName 指令提供的 formControlName 屬性把每個輸入框和 FormGroup 中定義的表單控件綁定起來。這些表單控件會和相應(yīng)的元素通訊,它們還把更改傳給 FormGroup,這個 FormGroup 是模型值的事實(shí)之源。

保存表單數(shù)據(jù)

ProfileEditor 組件從用戶那里獲得輸入,但在真實(shí)的場景中,你可能想要先捕獲表單的值,等將來在組件外部進(jìn)行處理。 FormGroup 指令會監(jiān)聽 form 元素發(fā)出的 submit 事件,并發(fā)出一個 ngSubmit 事件,讓你可以綁定一個回調(diào)函數(shù)。

onSubmit() 回調(diào)方法添加為 form 標(biāo)簽上的 ngSubmit 事件監(jiān)聽器。

Path:"src/app/profile-editor/profile-editor.component.html (submit event)" 。

<form [formGroup]="profileForm" (ngSubmit)="onSubmit()">

ProfileEditor 組件上的 onSubmit() 方法會捕獲 profileForm 的當(dāng)前值。要保持該表單的封裝性,就要使用 EventEmitter 向組件外部提供該表單的值。下面的例子會使用 console.warn 把這個值記錄到瀏覽器的控制臺中。

Path:"src/app/profile-editor/profile-editor.component.ts (submit method)" 。

onSubmit() {
  // TODO: Use EventEmitter with form value
  console.warn(this.profileForm.value);
}

form 標(biāo)簽所發(fā)出的 submit 事件是原生 DOM 事件,通過點(diǎn)擊類型為 submit 的按鈕可以觸發(fā)本事件。這還讓用戶可以用回車鍵來提交填完的表單。

往表單的底部添加一個 button,用于觸發(fā)表單提交。

Path:"src/app/profile-editor/profile-editor.component.html (submit button)" 。

<button type="submit" [disabled]="!profileForm.valid">Submit</button>

&上面這個代碼片段中的按鈕還附加了一個 disabled 綁定,用于在 profileForm 無效時禁用該按鈕。目前你還沒有執(zhí)行任何表單驗(yàn)證邏輯,因此該按鈕始終是可用的。稍后的驗(yàn)證表單輸入部分會講解基礎(chǔ)的表單驗(yàn)證。

顯示組件

要顯示包含此表單的 ProfileEditor 組件,請把它添加到組件模板中。

Path:"src/app/app.component.html (profile editor)" 。

<app-profile-editor></app-profile-editor>

ProfileEditor 讓你能管理 FormGroup 中的 firstNamelastNameFormControl 實(shí)例。

創(chuàng)建嵌套的表單組

表單組可以同時接受單個表單控件實(shí)例和其它表單組實(shí)例作為其子控件。這可以讓復(fù)雜的表單模型更容易維護(hù),并在邏輯上把它們分組到一起。

如果要構(gòu)建復(fù)雜的表單,如果能在更小的分區(qū)中管理不同類別的信息就會更容易一些。使用嵌套的 FormGroup 可以讓你把大型表單組織成一些稍小的、易管理的分組。

要制作更復(fù)雜的表單,請遵循如下步驟。

創(chuàng)建一個嵌套的表單組。

在模板中對這個嵌套表單分組。

某些類型的信息天然就屬于同一個組。比如名稱和地址就是這類嵌套組的典型例子,下面的例子中就用到了它們。

創(chuàng)建一個嵌套組

要在 profileForm 中創(chuàng)建一個嵌套組,就要把一個嵌套的 address 元素添加到此表單組的實(shí)例中。

Path:"src/app/profile-editor/profile-editor.component.ts (nested form group)" 。

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


@Component({
  selector: 'app-profile-editor',
  templateUrl: './profile-editor.component.html',
  styleUrls: ['./profile-editor.component.css']
})
export class ProfileEditorComponent {
  profileForm = new FormGroup({
    firstName: new FormControl(''),
    lastName: new FormControl(''),
    address: new FormGroup({
      street: new FormControl(''),
      city: new FormControl(''),
      state: new FormControl(''),
      zip: new FormControl('')
    })
  });
}

在這個例子中,address group 把現(xiàn)有的 firstName、lastName 控件和新的 street、citystatezip 控件組合在一起。雖然 address 這個 FormGroupprofileForm 這個整體 FormGroup 的一個子控件,但是仍然適用同樣的值和狀態(tài)的變更規(guī)則。來自內(nèi)嵌控件組的狀態(tài)和值的變更將會冒泡到它的父控件組,以維護(hù)整體模型的一致性。

在模板中對此嵌套表單分組

在修改了組件類中的模型之后,還要修改模板,來把這個 FormGroup 實(shí)例對接到它的輸入元素。

把包含 street、city、statezip 字段的 address 表單組添加到 ProfileEditor 模板中。

Path:"src/app/profile-editor/profile-editor.component.html (template nested form group)" 。

<div formGroupName="address">
  <h3>Address</h3>


  <label>
    Street:
    <input type="text" formControlName="street">
  </label>


  <label>
    City:
    <input type="text" formControlName="city">
  </label>

  
  <label>
    State:
    <input type="text" formControlName="state">
  </label>


  <label>
    Zip Code:
    <input type="text" formControlName="zip">
  </label>
</div>

ProfileEditor 表單顯示為一個組,但是將來這個模型會被進(jìn)一步細(xì)分,以表示邏輯分組區(qū)域。

注:

  • 這里使用了 value 屬性和 JsonPipe 管道在組件模板中顯示了這個 FormGroup 的值。

更新部分?jǐn)?shù)據(jù)模型

當(dāng)修改包含多個 FormGroup 實(shí)例的值時,你可能只希望更新模型中的一部分,而不是完全替換掉。這一節(jié)會講解該如何更新 AbstractControl 模型中的一部分。

有兩種更新模型值的方式:

  • 使用 setValue() 方法來為單個控件設(shè)置新值。 setValue() 方法會嚴(yán)格遵循表單組的結(jié)構(gòu),并整體性替換控件的值。

  • 使用 patchValue() 方法可以用對象中所定義的任何屬性為表單模型進(jìn)行替換。

setValue() 方法的嚴(yán)格檢查可以幫助你捕獲復(fù)雜表單嵌套中的錯誤,而 patchValue() 在遇到那些錯誤時可能會默默的失敗。

ProfileEditorComponent 中,使用 updateProfile 方法傳入下列數(shù)據(jù)可以更新用戶的名字與街道住址。

Path:"src/app/profile-editor/profile-editor.component.ts (patch value)" 。

updateProfile() {
  this.profileForm.patchValue({
    firstName: 'Nancy',
    address: {
      street: '123 Drew Street'
    }
  });
}

通過往模板中添加一個按鈕來模擬一次更新操作,以修改用戶檔案。

Path:"src/app/profile-editor/profile-editor.component.html (update value)" 。

<p>
  <button (click)="updateProfile()">Update Profile</button>
</p>

當(dāng)點(diǎn)擊按鈕時,profileForm 模型中只有 firstNamestreet 被修改了。注意,street 是在 address 屬性的對象中被修改的。這種結(jié)構(gòu)是必須的,因?yàn)?patchValue() 方法要針對模型的結(jié)構(gòu)進(jìn)行更新。patchValue() 只會更新表單模型中所定義的那些屬性。

使用 FormBuilder 服務(wù)生成控件

當(dāng)需要與多個表單打交道時,手動創(chuàng)建多個表單控件實(shí)例會非常繁瑣。FormBuilder 服務(wù)提供了一些便捷方法來生成表單控件。FormBuilder 在幕后也使用同樣的方式來創(chuàng)建和返回這些實(shí)例,只是用起來更簡單。

通過下列步驟可以利用這項(xiàng)服務(wù)。

導(dǎo)入 FormBuilder 類。

注入這個 FormBuilder 服務(wù)。

生成表單內(nèi)容。

下面的例子展示了如何重構(gòu) ProfileEditor 組件,用 FormBuilder 來代替手工創(chuàng)建這些 FormControlFormGroup 實(shí)例。

導(dǎo)入 FormBuilder 類

@angular/forms 包中導(dǎo)入 FormBuilder 類。

Path:"src/app/profile-editor/profile-editor.component.ts (import)" 。

import { FormBuilder } from '@angular/forms';

注入 FormBuilder 服務(wù)

FormBuilder 是一個可注入的服務(wù)提供者,它是由 ReactiveFormModule 提供的。只要把它添加到組件的構(gòu)造函數(shù)中就可以注入這個依賴。

Path:"src/app/profile-editor/profile-editor.component.ts (constructor)" 。

constructor(private fb: FormBuilder) { }

生成表單控件

FormBuilder 服務(wù)有三個方法:control()、group()array()。這些方法都是工廠方法,用于在組件類中分別生成 FormControl、FormGroupFormArray。

group 方法來創(chuàng)建 profileForm 控件。

Path:"src/app/profile-editor/profile-editor.component.ts (form builder)" 。

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


@Component({
  selector: 'app-profile-editor',
  templateUrl: './profile-editor.component.html',
  styleUrls: ['./profile-editor.component.css']
})
export class ProfileEditorComponent {
  profileForm = this.fb.group({
    firstName: [''],
    lastName: [''],
    address: this.fb.group({
      street: [''],
      city: [''],
      state: [''],
      zip: ['']
    }),
  });


  constructor(private fb: FormBuilder) { }
}

在上面的例子中,你可以使用 group() 方法,用和前面一樣的名字來定義這些屬性。這里,每個控件名對應(yīng)的值都是一個數(shù)組,這個數(shù)組中的第一項(xiàng)是其初始值。

&你可以只使用初始值來定義控件,但是如果你的控件還需要同步或異步驗(yàn)證器,那就在這個數(shù)組中的第二項(xiàng)和第三項(xiàng)提供同步和異步驗(yàn)證器。

比較一下用表單構(gòu)建器和手動創(chuàng)建實(shí)例這兩種方式。

  1. Path:"src/app/profile-editor/profile-editor.component.ts (instances)" 。

    profileForm = new FormGroup({
      firstName: new FormControl(''),
      lastName: new FormControl(''),
      address: new FormGroup({
        street: new FormControl(''),
        city: new FormControl(''),
        state: new FormControl(''),
        zip: new FormControl('')
      })
    });

  1. Path:"src/app/profile-editor/profile-editor.component.ts (form builder)" 。

    profileForm = this.fb.group({
      firstName: [''],
      lastName: [''],
      address: this.fb.group({
        street: [''],
        city: [''],
        state: [''],
        zip: ['']
      }),
    });

驗(yàn)證表單輸入

表單驗(yàn)證用于確保用戶的輸入是完整和正確的。本節(jié)講解了如何把單個驗(yàn)證器添加到表單控件中,以及如何顯示表單的整體狀態(tài)。表單驗(yàn)證的更多知識在表單驗(yàn)證一章中有詳細(xì)的講解。

使用下列步驟添加表單驗(yàn)證。

  1. 在表單組件中導(dǎo)入一個驗(yàn)證器函數(shù)。

  1. 把這個驗(yàn)證器添加到表單中的相應(yīng)字段。

  1. 添加邏輯來處理驗(yàn)證狀態(tài)。

最常見的驗(yàn)證是做一個必填字段。下面的例子給出了如何在 firstName 控件中添加必填驗(yàn)證并顯示驗(yàn)證結(jié)果的方法。

導(dǎo)入驗(yàn)證器函數(shù)

響應(yīng)式表單包含了一組開箱即用的常用驗(yàn)證器函數(shù)。這些函數(shù)接收一個控件,用以驗(yàn)證并根據(jù)驗(yàn)證結(jié)果返回一個錯誤對象或空值。

@angular/forms 包中導(dǎo)入 Validators 類。

Path:"src/app/profile-editor/profile-editor.component.ts (import)" 。

import { Validators } from '@angular/forms';

建一個必填字段

ProfileEditor 組件中,把靜態(tài)方法 Validators.required 設(shè)置為 firstName 控件值數(shù)組中的第二項(xiàng)。

Path:"src/app/profile-editor/profile-editor.component.ts (import)" 。

profileForm = this.fb.group({
  firstName: ['', Validators.required],
  lastName: [''],
  address: this.fb.group({
    street: [''],
    city: [''],
    state: [''],
    zip: ['']
  }),
});

HTML5 有一組內(nèi)置的屬性,用來進(jìn)行原生驗(yàn)證,包括 required、minlengthmaxlength 等。雖然是可選的,不過你也可以在表單的輸入元素上把它們添加為附加屬性來使用它們。這里我們把 required 屬性添加到 firstName 輸入元素上。

Path:"src/app/profile-editor/profile-editor.component.html (required attribute)" 。

<input type="text" formControlName="firstName" required>

注:

  • 這些 HTML5 驗(yàn)證器屬性可以和 Angular 響應(yīng)式表單提供的內(nèi)置驗(yàn)證器組合使用。組合使用這兩種驗(yàn)證器實(shí)踐,可以防止在模板檢查完之后表達(dá)式再次被修改導(dǎo)致的錯誤。

顯示表單狀態(tài)

當(dāng)你往表單控件上添加了一個必填字段時,它的初始值是無效的(invalid)。這種無效狀態(tài)會傳播到其父 FormGroup 元素中,也讓這個 FormGroup 的狀態(tài)變?yōu)闊o效的。你可以通過該 FormGroup 實(shí)例的 status 屬性來訪問其當(dāng)前狀態(tài)。

使用插值顯示 profileForm 的當(dāng)前狀態(tài)。

Path:"src/app/profile-editor/profile-editor.component.html (display status)" 。

<p>
  Form Status: {{ profileForm.status }}
</p>

提交按鈕被禁用了,因?yàn)?firstName 控件的必填項(xiàng)規(guī)則導(dǎo)致了 profileForm 也是無效的。在你填寫了 firstName 輸入框之后,該表單就變成了有效的,并且提交按鈕也啟用了。

創(chuàng)建動態(tài)表單

FormArrayFormGroup 之外的另一個選擇,用于管理任意數(shù)量的匿名控件。像 FormGroup 實(shí)例一樣,你也可以往 FormArray 中動態(tài)插入和移除控件,并且 FormArray 實(shí)例的值和驗(yàn)證狀態(tài)也是根據(jù)它的子控件計算得來的。 不過,你不需要為每個控件定義一個名字作為 key,因此,如果你事先不知道子控件的數(shù)量,這就是一個很好的選擇。

要定義一個動態(tài)表單,請執(zhí)行以下步驟。

  1. 導(dǎo)入 FormArray 類。

  1. 定義一個 FormArray 控件。

  1. 使用 getter 方法訪問 FormArray 控件。

  1. 在模板中顯示這個表單數(shù)組。

下面的例子展示了如何在 ProfileEditor 中管理別名數(shù)組。

導(dǎo)入 FormArray 類

@angular/form 中導(dǎo)入 FormArray,以使用它的類型信息。FormBuilder 服務(wù)用于創(chuàng)建 FormArray 實(shí)例。

Path:"src/app/profile-editor/profile-editor.component.ts (import)" 。

import { FormArray } from '@angular/forms';

定義 FormArray 控件

你可以通過把一組(從零項(xiàng)到多項(xiàng))控件定義在一個數(shù)組中來初始化一個 FormArray。為 profileForm 添加一個 aliases 屬性,把它定義為 FormArray 類型。

使用 FormBuilder.array() 方法來定義該數(shù)組,并用 FormBuilder.control() 方法來往該數(shù)組中添加一個初始控件。

Path:"src/app/profile-editor/profile-editor.component.ts (aliases form array)" 。

profileForm = this.fb.group({
  firstName: ['', Validators.required],
  lastName: [''],
  address: this.fb.group({
    street: [''],
    city: [''],
    state: [''],
    zip: ['']
  }),
  aliases: this.fb.array([
    this.fb.control('')
  ])
});

FormGroup 中的這個 aliases 控件現(xiàn)在管理著一個控件,將來還可以動態(tài)添加多個。

訪問 FormArray 控件

相對于重復(fù)使用 profileForm.get() 方法獲取每個實(shí)例的方式,getter 可以讓你輕松訪問表單數(shù)組各個實(shí)例中的別名。 表單數(shù)組實(shí)例用一個數(shù)組來代表未定數(shù)量的控件。通過 getter 來訪問控件很方便,這種方法還能很容易地重復(fù)處理更多控件。

使用 getter 語法創(chuàng)建類屬性 aliases,以從父表單組中接收表示綽號的表單數(shù)組控件。

Path:"src/app/profile-editor/profile-editor.component.ts (aliases getter)" 。

get aliases() {
  return this.profileForm.get('aliases') as FormArray;
}

注:

  • 因?yàn)榉祷氐目丶念愋褪?code>AbstractControl,所以你要為該方法提供一個顯式的類型聲明來訪問 FormArray 特有的語法。

定義一個方法來把一個綽號控件動態(tài)插入到綽號 FormArray 中。用 FormArray.push() 方法把該控件添加為數(shù)組中的新條目。

Path:"src/app/profile-editor/profile-editor.component.ts (add alias)" 。

addAlias() {
  this.aliases.push(this.fb.control(''));
}

在這個模板中,這些控件會被迭代,把每個控件都顯示為一個獨(dú)立的輸入框。

在模板中顯示表單數(shù)組

要想為表單模型添加 aliases,你必須把它加入到模板中供用戶輸入。和 FormGroupNameDirective 提供的 formGroupName 一樣,FormArrayNameDirective 也使用 formArrayName 在這個 FormArray 實(shí)例和模板之間建立綁定。

formGroupName <div> 元素的結(jié)束標(biāo)簽下方,添加一段模板 HTML。

Path:"src/app/profile-editor/profile-editor.component.html (aliases form array template)" 。

<div formArrayName="aliases">
  <h3>Aliases</h3> <button (click)="addAlias()">Add Alias</button>


  <div *ngFor="let alias of aliases.controls; let i=index">
    <!-- The repeated alias template -->
    <label>
      Alias:
      <input type="text" [formControlName]="i">
    </label>
  </div>
</div>

*ngFor 指令對 aliases FormArray 提供的每個 FormControl 進(jìn)行迭代。因?yàn)?FormArray 中的元素是匿名的,所以你要把索引號賦值給 i 變量,并且把它傳給每個控件的 formControlName 輸入屬性。

每當(dāng)新的 alias 加進(jìn)來時,FormArray 的實(shí)例就會基于這個索引號提供它的控件。這將允許你在每次計算根控件的狀態(tài)和值時跟蹤每個控件。

添加一個別名

最初,表單只包含一個綽號字段,點(diǎn)擊 Add Alias 按鈕,就出現(xiàn)了另一個字段。你還可以驗(yàn)證由模板底部的“Form Value”顯示出來的表單模型所報告的這個綽號數(shù)組。

注:

  • 除了為每個綽號使用 FormControl 之外,你還可以改用 FormGroup 來組合上一些額外字段。對其中的每個條目定義控件的過程和前面沒有區(qū)別。

響應(yīng)式表單 API 匯總

下列表格給出了用于創(chuàng)建和管理響應(yīng)式表單控件的基礎(chǔ)類和服務(wù)。

說明
AbstractControl 所有三種表單控件類(FormControl、FormGroup 和 FormArray)的抽象基類。它提供了一些公共的行為和屬性。
FormControl 管理單體表單控件的值和有效性狀態(tài)。它對應(yīng)于 HTML 的表單控件,比如 <input> 或 <select>。
FormGroup 管理一組 AbstractControl 實(shí)例的值和有效性狀態(tài)。該組的屬性中包括了它的子控件。組件中的頂層表單就是 FormGroup
FormArray 管理一些 AbstractControl 實(shí)例數(shù)組的值和有效性狀態(tài)。
FormBuilder 一個可注入的服務(wù),提供一些用于提供創(chuàng)建控件實(shí)例的工廠方法。

指令

指令 說明
FormControlDirective 把一個獨(dú)立的 FormControl 實(shí)例綁定到表單控件元素。
FormControlName 把一個現(xiàn)有 FormGroup 中的 FormControl 實(shí)例根據(jù)名字綁定到表單控件元素。
FormGroupDirective 把一個現(xiàn)有的 FormGroup 實(shí)例綁定到 DOM 元素。
FormGroupName 把一個內(nèi)嵌的 FormGroup 實(shí)例綁定到一個 DOM 元素。
FormArrayName 把一個內(nèi)嵌的 FormArray 實(shí)例綁定到一個 DOM 元素。
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號