前言
承上篇,接著使用 FormBuilder 重構程式碼。
使用 FormBuilder 來產生表單控制元件
當需要與多個表單打交道時,手動建立多個表單控制元件例項會非常繁瑣。
FormBuilder 服務提供了一些便捷方法來產生表單控制元件,在幕後也使用同樣的方式來建立和返回這些實例,只是用起來更簡單。
接下來會重構 ProfileEditor 元件,用 FormBuilder 來建立這些 FormControl 和 FormGroup 實例。
匯入 FormBuilder 類
從 @angular/forms 包中匯入 FormBuilder 類。
1 | import { FormGroup, FormControl, FormBuilder } from '@angular/forms'; |
注入 FormBuilder 服務
FormBuilder 是一個可注入的服務提供商,它是由 ReactiveFormModule 提供的。
只要把它新增到元件的建構函式中就可以注入這個依賴。
1 | constructor(private fb: FormBuilder) { } |
產生表單控制元件
FormBuilder 服務有三個方法:
- control()
- group()
- array()
這些方法都是工廠方法,用於在元件的 class 中分別產生 FormControl 、 FormGroup 和 FormArray。
所以可以使用 group 方法建立 profileForm 控制元件。
而目前 profile-editor.component 的 class 是這樣的
1 | import { Component, OnInit } from '@angular/core'; |
不難看出與先前手動建立 FormControl 、 FormGroup 十分相似,而且省下了非常大量的 new 。
每個控制元件名對應的值都是一個陣列,而陣列中的第一項是其初始值。
可以只使用初始值來定義控制元件,但是如果控制元件還需要同步或非同步驗證器,那就在這個陣列中的第二項和第三項提供同步和非同步驗證器。
運行看看重構的結果吧!
簡易表單驗證
表單驗證用於驗證使用者的輸入,以確保其完整和正確。
那麼該如何把單個驗證器新增到表單控制元件中,以及如何顯示表單的整體狀態呢?
匯入驗證器函式
響應式表單包含了一組內建的常用驗證器函式。
這些函式接收一個控制元件,用以驗證並根據驗證結果返回一個錯誤物件或空值。
1 | import { Validators } from '@angular/forms'; |
把欄位設為必填(required)
檢查某個欄位有沒有被正確的填入值是相當常見的驗證:
- 把 firstName 設為必填項目
- 在 ProfileEditor 元件中,把靜態方法 Validators.required 設定為 firstName 控制元件值陣列中的第二項。
1 | profileForm = this.fb.group({ |
然後使用內嵌繫結觀察表單的驗證狀態。
1 | <p> |
而我們也可以搭配原生的 HTML5 驗證屬性,可以防止在 Template 檢查完之後表示式再次被修改導致的錯誤。
1 | <input type="text" formControlName="firstName" required> |
可以做得更好!像是把驗證有沒有通過的狀態繫結在提交按鈕的 disabled 屬性上。
1 | <button type="submit" [disabled]="!profileForm.valid">提交</button> |
提交按鈕被禁用了,因為 firstName 控制元件的必填項規則導致了 profileForm 也是無效的。
使用表單陣列管理動態控制元件
FormArray 是 FormGroup 之外的另一個選擇,用於管理任意數量的匿名控制元件。
像 FormGroup 實例一樣,可以在 FormArray 中動態插入和移除控制元件,並且 FormArray 實例的值和驗證狀態也是根據它的子控制元件計算得來的。
FormArray 與 FormGroup 差別在於不需要為每個控制元件定義一個名字作為 key。
因此,如果不知道子控制元件的數量,這就是一個很好的選擇。
舉例來說,我們可以加入綽號的部分,因為我們不知道綽號會有幾個。
匯入 FormArray
從 @angular/form 中匯入 FormArray,以使用它的型別資訊。
1 | import { FormArray } from '@angular/forms'; |
定義 FormArray
可以透過把一組(從零項到多項)控制元件定義在一個陣列中藉以初始化一個 FormArray。
為 profileForm 新增一個 alias 屬性,把它定義為 FormArray 型別。
使用 FormBuilder.array() 方法來定義該陣列,並用 FormBuilder.control() 方法來往該陣列中新增一個初始控制元件。
1 | profileForm = this.fb.group({ |
現在 FormGroup 中的這個 alias 控制元件現在管理著一個控制元件,將來還可以動態新增多個。
訪問 FormArray 控制元件
相對於重復使用 profileForm.get() 方法獲取每個例項的方式, getter 可以讓你輕鬆訪問 FormArray 實例中的綽號。
FormArray 實例用一個陣列來代表未定數量的控制元件。
透過 getter 來訪問控制元件很方便,這種方法還能很容易地重複處理更多控制元件。
使用 getter 語法建立類屬性 aliases,以從父表單組中接收表示 aliases 的 FormArray 控制元件。
1 | get aliases() { |
因為 return 的控制元件的型別是 AbstractControl,所以要為該方法提供一個顯式的型別宣告來訪問 FormArray 特有的語法。
宣告 addAlias 方法來把一個控制元件動態插入到 aliases 的 FormArray 中:
- 用 FormArray.push() 方法把該控制元件新增為陣列中的新元素
1 | addAlias() { |
在 Template 中,這些控制元件會被迭代,把每個控制元件都顯示為一個獨立的輸入框。
在 Template 中顯示 FormArray
使用 formArrayName 在這個 FormArray 例項和範本之間建立繫結。
1 | <div formArrayName="aliases"> |
使用 ngFor 指令對 aliases FormArray 提供的每個 FormControl 進行迭代。
因為 FormArray 中的元素是匿名的,所以要把索引號賦值給 i 變數,並且把它傳給每個控制元件的 formControlName 輸入屬性。
每當新的 alias 加進來時,FormArray 的實例就會基於這個索引號提供它的控制元件。
至此,我們完成了響應式表單的基礎範例練習。