[Angular] Forms – Reactive Forms – iT 邦幫忙::一起幫忙解決難題,拯救 IT 人的一天
Mục Lục
前言
Reactive forms提供了一種model-driven
的方法來處理表單中會隨時間變化的輸入(數據),本篇中介紹著如何控制與更新一個表單、使用多個form-group、表單驗證等等的基本表單操作。
Overview of reactive forms
Reactive forms使用了顯式且不變
的方法管理表單狀況,對於表單的狀態的更改每次都會回傳一個新的表單狀態
,由於每次都會回傳一個新的狀態,所以他可以保持每次該改之間的model完整性,Reactive forms是圍繞著observable
所建立的,表單中的輸入(數值)會提供給input flow作為他的值並可以以同步的方式訪問。
Reactive forms與template forms不同,Reactive forms通過對數據模型的同步訪問提供了較高的可預測性
、observable operators的不變性
和可以透過observable streams觀察數據的變更
。
Adding a basic form control
建立Reactive forms的三個步驟:
- 在你的app中註冊
reactive foms module
,這個module中含有使用Reactive forms的指令。 - 在Component中新增一個新的
FormControl instance
。 - 在templete中註冊FormControl。
Register the reactive forms module
使用import關鍵字將ReactiveFormsModule
添加到app.module中的import陣列中。
// src/app/app.module.ts
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
imports: [
// other imports ...
ReactiveFormsModule
],
})
export class AppModule { }
接下來透過cli建立一個 name-editor
Component,並將FormControl
class加入到Component中並將他實體化。
// src/app/name-editor/name-editor.component.ts
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms'; // import FormControl Class
@Component({
selector: 'app-name-editor',
templateUrl: './name-editor.component.html',
styleUrls: ['./name-editor.component.css']
})
export class NameEditorComponent {
name = new FormControl(''); // instance FormControl class
}
Register the control in the template
在name-editor
的templete中建立可以與表單控制元件連動的網頁元件,使用formControl
綁定templete中的元件。
<!-- src/app/name-editor/name-editor.component.html -->
<label>
Name:
<input type="text" [formControl]="name">
</label>
在畫面中顯示出來
<!-- src/app/app.component.html -->
<app-name-editor></app-name-editor>
Displaying a form control value
可以透過兩種方法顯示forms中的數值:
- 可以透過
valueChange()
中的subscription()
方法監聽表單中的值。 - 使用FormControl中的
value
屬性。
name = new FormControl('');
this.name.valueChanges.subscribe(val => console.log(val));
Value : {{name.value}}
Grouping form controls
一個表單中會包含幾個相關的控制元件,Reactive forms提供兩種方法可以將多個相關的控制組件分組為單個輸入形式
。
- 使用form group定義一組固定的控制組件表單,可以統一管理這些類似的表單組件。
- 使用form array定義一個動態的表單,可以隨時添加或刪除控制組件。
透過cli建立一個新組建,使用import將FormGroup
與FormControl
加入Component中並將他們實例化。
// src/app/profile-editor/profile-editor.component.ts (form group)
import { Component } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms'; //import two class
@Component({
selector: 'app-profile-editor',
templateUrl: './profile-editor.component.html',
styleUrls: ['./profile-editor.component.css']
})
export class ProfileEditorComponent {
// instance FormGroup & FormControl
profileForm = new FormGroup({
firstName: new FormControl(''),
lastName: new FormControl(''),
});
}
將firstName
與lastName
這兩個相關的控制組件透過FormGroup
分組為一個群組。
Create a nested groups
可以透過嵌套FromGroups來達到將較為複雜的表單內容分割為更小的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('')
})
});
}
將原本的profileForm Group中新增了第二層的嵌套Group,將street、city、state、zip綁定為一個FormGroup,透過這樣的綁定可以將原本的profileForm保持完整的前提下再新增出一個Group。
<form [formGroup]="profileForm">
<label>
First Name:
<input type="text" formControlName="firstName">
</label>
<label>
Last Name:
<input type="text" formControlName="lastName">
</label>
<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>
<button type="submit" [disabled]="!profileForm.valid">Submit</button>
</form>
Updating parts of the data model
當需要更新多個控制組件的值時,Angular提供兩種方法可以改變值,一種是可以更改整組表單的方法,另一種是更改表單部分內容的方法。
- 使用
setValue()
可以更新整個表單內容,他必須要遵守整個表單的結構
。 - 使用
patchValue()
可以替換表單內的部分內容。
使用setValue()會嚴格檢查整個表單的結構,若結構錯誤則會發生嵌套錯誤,若是使用patchValue()在這個錯誤上會默默地失敗。
updateProfile() {
this.profileForm.patchValue({
firstName: 'Nancy',
address: {
street: '123 Drew Street'
}
});
}
Using the FormBuilder service to generate controls
對於在處理多個表單的時候還是使用手動的方式創建表單控制組件實例,這樣的話可能會遇到重複宣告的問題,所以可以使用FormBuilder
,它提供了用於生成控制組件的便利方法。
- import FormBuilder Class.
- Inject the FormBuilder service.
- Generate the form contents.
Import the FormBuilder class
import { FormBuilder } from '@angular/forms';
Inject the FormBuilder service
在Component的comstructor中Inject FormBuilder service
。
constructor(private fb: FormBuilder) { }
Generate form controls
FormBuilder提供control()
、group()
、array()
這三個方法,他們用於生成實例包括FormControl、FormGroup、FromArray。
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) { }
}
Creating dynamic forms
FormArray是用於管理任何數量的未命名控件的FormGroup的替代方法,可以動態的插入或刪除,所以不知道子值的數量時可以使用FormArray來代替FormGroup。
- Import the FormArray class.
- Define a FormArray control.
- 使用get()訪問FormArray control.
- Display the form array in a template.
Import the FormArray class
import { FormArray } from '@angular/forms';
Define a FormArray control
通過在數組中定義控件,可以使用從零到許多的任意數量的控件來初始化表單數組,使用FormBuilder.array()
定義陣列並使用FormBuilder.control()
初始化控件填充數組。
profileForm = this.fb.group({
firstName: ['', Validators.required],
lastName: [''],
address: this.fb.group({
street: [''],
city: [''],
state: [''],
zip: ['']
}),
aliases: this.fb.array([
this.fb.control('')
])
});
Access the FormArray control
可以使用getter
可以輕鬆訪問表單陣列的實例的別名。
get aliases() {
return this.profileForm.get('aliases') as FormArray;
}
還可以使用FormArray.push()
將表單數據動態的插入到現有表單中。
addAlias() {
this.aliases.push(this.fb.control(''));
}
Display the form array in the 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中的所有表單實例,因為表單數組元素未命名,所以您將索引分配給i變量,並將其傳遞給每個控件以將其綁定到formControlName輸入。
Reactive forms API summary
Classes
Class
Description
AbstractControl
FormControl,FormGroup和FormArray的抽象基類,它提供了常見的行為和屬性。
FormControl
管理單個表單空間的值和有效性的狀態,他對應到HTML的表單控件<input>
或<select>
FormGroup
管理一組AbstractControl實例的值和有效狀態,它包含他這個FormGroup的子FormControl。
FormArray
管理AbstractControl實例的陣列的值有效性。
FormBuilder
用於創建FormControl的方法。
Directives
Directive
Description
FormControlDirective
將獨立的FormControl實例同步到表單控件元素。
FormControlName
通過名稱將現有FormGroup實例中的FormControl同步到表單控件元素。
FormGroupDirective
將現有的FormGroup實例同步到DOM元素。
FormGroupName
將嵌套的FormGroup實例同步到DOM元素。
FormArrayName
將嵌套的FormArray實例同步到DOM元素。
參考文獻:
Angular – Reactive Forms