响应式表单(Reactive Forms)是 Angular 中处理表单的一种方式,使用时表单的控件(如输入框、选择框等)以及表单的结构都由代码控制,而不是由模板驱动。响应式表单提供了更强大的功能,适合用于复杂的表单操作,如动态表单、条件验证、表单值的处理等。
响应式表单的核心思想是 通过代码来定义和管理表单结构,并且可以直接操作表单数据和状态,适用于需要更多控制和自定义的场景。
1. 响应式表单的基本概念
响应式表单由以下几个主要部分组成:
- FormControl:表示表单控件(例如,输入框、复选框等)的单个值和状态。
- FormGroup:是一个包含多个
FormControl
或FormGroup
的集合,表示一个表单的结构。 - FormArray:是一个
FormControl
的集合,适用于有多个类似控件的场景(例如,动态生成多个表单控件)。 - Validators:表单验证规则,用来控制表单控件的合法性。
- FormBuilder:是一个简化表单创建的服务,提供了一些方便的方法来构建表单控件和表单组。
2. 创建响应式表单
响应式表单通常通过 FormControl
、FormGroup
和 FormArray
来定义表单的控件和结构。
2.1 安装和导入 ReactiveFormsModule
在使用响应式表单之前,需要在 Angular 模块中导入 ReactiveFormsModule
:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms'; // 导入 ReactiveFormsModule
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, ReactiveFormsModule], // 添加到 imports 中
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
2.2 创建表单控件(FormControl)
FormControl
是 Angular 响应式表单的基本单位,表示一个表单控件的值和状态。例如:
import { Component, OnInit } from '@angular/core';
import { FormControl, Validators } from '@angular/forms';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {
username: FormControl;
ngOnInit() {
this.username = new FormControl('', [Validators.required, Validators.minLength(3)]);
}
}
在这个例子中,我们创建了一个 FormControl
实例 username
,它有两个验证器:Validators.required
和 Validators.minLength(3)
。
2.3 创建表单组(FormGroup)
FormGroup
是用来管理多个 FormControl
或其他 FormGroup
的集合,表示一个完整的表单。一个表单通常由多个控件组成,因此我们使用 FormGroup
来组合这些控件。
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {
myForm: FormGroup;
ngOnInit() {
this.myForm = new FormGroup({
username: new FormControl('', [Validators.required, Validators.minLength(3)]),
email: new FormControl('', [Validators.required, Validators.email]),
age: new FormControl('', [Validators.required, Validators.min(18)]),
});
}
onSubmit() {
console.log(this.myForm.value); // 提交表单数据
}
}
在这个例子中,myForm
是一个 FormGroup
,包含了三个 FormControl
控件:username
、email
和 age
。每个控件都有自己的验证规则。
2.4 使用 FormBuilder 简化表单创建
Angular 提供了 FormBuilder
服务,可以简化 FormGroup
和 FormControl
的创建过程。
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {
myForm: FormGroup;
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.myForm = this.fb.group({
username: ['', [Validators.required, Validators.minLength(3)]],
email: ['', [Validators.required, Validators.email]],
age: ['', [Validators.required, Validators.min(18)]],
});
}
onSubmit() {
console.log(this.myForm.value); // 提交表单数据
}
}
通过 FormBuilder
,我们可以使用简洁的语法来创建表单控件和表单组。
3. 验证器
表单验证是响应式表单的一个重要特性,Angular 提供了许多内置的验证器,如:
Validators.required
:要求该控件的值不能为空。Validators.minLength(length)
:要求控件的值具有最小长度。Validators.maxLength(length)
:要求控件的值不超过最大长度。Validators.email
:要求控件的值为有效的电子邮件地址。Validators.min(value)
:要求控件的值大于或等于指定值。
3.1 验证器的使用
验证器可以在创建 FormControl
或 FormGroup
时应用。例如:
username: new FormControl('', [
Validators.required,
Validators.minLength(3)
])
3.2 自定义验证器
除了内置的验证器,Angular 还允许我们创建自定义验证器。自定义验证器是一个返回 ValidationErrors
对象的函数。以下是一个简单的自定义验证器,它检查输入值是否为正数:
import { AbstractControl, ValidationErrors } from '@angular/forms';
export function positiveNumberValidator(control: AbstractControl): ValidationErrors | null {
if (control.value <= 0) {
return { 'positiveNumber': { value: control.value } };
}
return null;
}
然后将它应用于 FormControl
:
age: new FormControl('', [positiveNumberValidator])
3.3 异步验证器
有时需要执行异步验证,比如检查用户名是否已经存在。可以通过异步验证器来完成。异步验证器通常返回一个 Observable
,并在表单控件的值变化时进行验证。
import { Observable } from 'rxjs';
import { debounceTime, map, switchMap } from 'rxjs/operators';
function checkUsernameExistence(control: AbstractControl): Observable<ValidationErrors | null> {
return this.usernameService.checkUsername(control.value).pipe(
debounceTime(300),
map(isTaken => (isTaken ? { 'usernameTaken': true } : null))
);
}
4. 表单状态和方法
响应式表单提供了许多有用的属性和方法来管理表单状态和交互。
4.1 表单控件的状态
valid
:表单是否有效,所有控件都有效。invalid
:表单是否无效。pristine
:表单是否未修改。dirty
:表单是否被修改。touched
:控件是否被触摸。untouched
:控件是否未被触摸。pending
:表单控件是否正在等待异步验证的结果。
4.2 表单方法
markAsTouched()
:将控件标记为已触摸。markAsDirty()
:将控件标记为已修改。reset()
:重置控件的值和状态。setValue()
:设置控件的值。patchValue()
:部分更新控件的值。
this.myForm.get('username').setValue('John Doe');
5. 响应式表单的优势
- 更强的灵活性和控制力:与模板驱动表单相比,响应式表单提供了更高的灵活性和控制力,尤其适合复杂和动态的表单。
- 易于测试:响应式表单通过 JavaScript 代码来定义和管理表单,方便进行单元测试。
- 适用于动态表单:响应式表单非常适合处理动态添加/删除表单控件的情况。
- 同步和异步验证:响应式表单允许使用同步和异步验证器,并且可以轻松地管理表单验证逻辑。
6. 总结
- 响应式表单 使用
FormControl
、FormGroup
和FormArray
来定义表单的结构和控件,并通过代码控制表单的行为。 - 验证器 用于控制表单控件的合法性,可以使用内置的验证器,也可以创建自定义和异步验证器。
- 响应式表单提供了更强大的功能和灵活性,适用于需要更多控制和动态表单操作的场景。