Reactive Forms 巢狀表單結構
在實際應用中,我們常常需要將表單控制項分組,以便更好地組織和處理相關聯的資料。Angular 的 Reactive Forms 提供了巢狀 FormGroup 結構來實現這一點。
TypeScript 中的巢狀結構
在 TypeScript 中,可以在一個 FormGroup 內嵌套另一個 FormGroup,把想要放在同一群組的 FormControl 放在一起:
registerForm = new FormGroup({
email: new FormControl('', {
validators: [Validators.required, Validators.email],
}),
passwords: new FormGroup({
password: new FormControl('', {
validators: [Validators.required, Validators.minLength(6)],
}),
confirmPassword: new FormControl('', {
validators: [Validators.required, Validators.minLength(6)],
}),
}),
});
在這個例子中,passwords
是一個嵌套的 FormGroup,包含了 password
和 confirmPassword
兩個 FormControl。
範本中的巢狀結構
在 HTML 範本中,也需要反映這種嵌套結構。有兩種方式可以實現:
方式一:使用 [formGroup]
<div class="control-row" [formGroup]="registerForm.controls.passwords">
<div class="control">
<label for="password">Password</label>
<input
id="password"
type="password"
name="password"
formControlName="password"
/>
</div>
<div class="control">
<label for="confirm-password">Confirm Password</label>
<input
id="confirm-password"
type="password"
name="confirm-password"
formControlName="confirmPassword"
/>
</div>
</div>
方式二:使用 formGroupName
<div class="control-row" formGroupName="passwords">
<div class="control">
<label for="password">Password</label>
<input
id="password"
type="password"
name="password"
formControlName="password"
/>
</div>
<div class="control">
<label for="confirm-password">Confirm Password</label>
<input
id="confirm-password"
type="password"
name="confirm-password"
formControlName="confirmPassword"
/>
</div>
</div>
兩種方式的差異
[formGroup]:
需要直接引用 TypeScript 中的 FormGroup 實例
使用屬性綁定語法
更適合動態產生的表單結構
formGroupName:
使用字串指定巢狀 FormGroup 的名稱
語法更簡潔
必須在父 FormGroup 的上下文中使用
在多數情況下,formGroupName
寫法更為常用和簡潔。
存取巢狀表單的值
巢狀表單會影響如何存取和設置表單值:
// 存取整個表單的值
console.log(this.registerForm.value);
// 輸出: { email: '...', passwords: { password: '...', confirmPassword: '...' } }
// 存取巢狀群組的值
console.log(this.registerForm.get('passwords').value);
// 輸出: { password: '...', confirmPassword: '...' }
// 存取巢狀控制項的值
console.log(this.registerForm.get('passwords.password').value);
// 或
console.log(this.registerForm.controls.passwords.get('password').value);
巢狀表單的驗證
除了對個別控制項進行驗證,我們也可以對整個巢狀 FormGroup 進行驗證:
registerForm = new FormGroup({
email: new FormControl('', [...]),
passwords: new FormGroup({
password: new FormControl('', [...]),
confirmPassword: new FormControl('', [...])
}, { validators: this.passwordsMatch })
});
// 驗證兩個密碼是否匹配
passwordsMatch(group: FormGroup): ValidationErrors | null {
const password = group.get('password').value;
const confirmPassword = group.get('confirmPassword').value;
return password === confirmPassword ? null : { passwordsDoNotMatch: true };
}
在範本中檢查 FormGroup 級別的錯誤:
<div formGroupName="passwords">
<!-- 輸入欄位 -->
@if(registerForm.controls.passwords.errors?.['passwordsDoNotMatch']){
<p class="error">Passwords do not match!</p>
}
</div>
修改巢狀表單的值
使用 patchValue
或 setValue
時也需要考慮巢狀結構:
// 部分更新
this.registerForm.patchValue({
email: 'test@example.com',
passwords: {
password: '123456'
// 不需要包含 confirmPassword
}
});
// 完整更新
this.registerForm.setValue({
email: 'test@example.com',
passwords: {
password: '123456',
confirmPassword: '123456' // 必須包含所有控制項
}
});
巢狀表單的好處
邏輯分組:將相關的控制項組織在一起,提高代碼可讀性
結構化數據:表單值對象結構更清晰,方便處理
群組驗證:可以對相關控制項進行整體驗證
更好的維護性:大型表單更容易管理和維護
巢狀表單結構是處理複雜表單的有力工具,能夠讓表單結構更加清晰和有組織。

Last updated