angular 表单介绍之响应式表单
响应式表单
响应式表单与模板驱动表单有着显著的不同点。响应式表单通过对数据模型的同步访问提供了更多的可预测性,使用 Observable 的操作符提供了不可变性,并且通过 Observable 流提供了变化追踪功能。
模板驱动表单允许你直接在模板中修改数据,但不像响应式表单那么明确,因为它们依赖嵌入到模板中的指令,并借助可变数据来异步跟踪变化。
表单的控制逻辑写在组件类中,对验证逻辑拥有更多的控制权,适合复杂的表单的类型。
基础类
| 类 | 详情 |
|---|---|
| AbstractControl | 所有三种表单控件类(FormControl、FormGroup 和 FormArray)的抽象基类。它提供了一些公共的行为和属性。 |
| FormControl | 管理单体表单控件的值和有效性状态。它对应于 HTML 的表单控件,比如 或 。 |
| FormGroup | 管理一组 AbstractControl 实例的值和有效性状态。该组的属性中包括了它的子控件。组件中的顶层表单就是 FormGroup。 |
| FormArray | 管理一些 AbstractControl 实例数组的值和有效性状态。 |
| FormBuilder | 一个可注入的服务,提供一些用于提供创建控件实例的工厂方法。 |
| FormRecord | 跟踪 FormControl 实例集合的值和有效性状态,每个实例都具有相同的值类型。 |
快速开始
准备
在对应组件module中引入ReactiveFormsModule
- 若使用响应式表单,则导入
ReactiveFormsModule。- 若使用模板驱动式表单,则导入
FormsModule。
在组件类中创建 FormGroup 表单控制对象
form: FormGroup
constructor(private fb: FormBuilder, private messageService: MessageService) {
this.form = this.fb.group({
name: ['', [Validators.pattern('^[a-zA-Z][a-zA-Z0-9]{3,15}$')]],
sex: ['', [Validators.required]],
birthDate: ['', [Validators.required]],
educationLevel: ['', [Validators.required]],
tel: [null, [Validators.pattern('^1(3|4|5|7|8)\d{9}$')]],
confirmTel: ['', [Validators.required, this.confirmationValidator]],
contacts: new FormArray([
this.fb.group({
username: new FormControl(),
address: new FormControl(),
phone: new FormControl()
})
])
})
}
组件模板创建表单
<form [formGroup]="form"></form>
添加表单控件
<div class="grid">
<div class="col-6 col-offset-2">
<h4>响应式表单</h4>
<form [formGroup]="form">
<div class="card p-fluid">
<div class="field grid">
<label
htmlFor="name"
class="col-12 mb-3 md:col-3 md:mb-0 text-xl justify-content-end"
>姓名:<span style="color: firebrick">*</span></label
>
<div class="col-12 md:col-9">
<input
pInputText
id="name"
type="text"
name="name"
placeholder="真实姓名"
formControlName="name"
/>
<!-- *ngIf="validateForm.get('tel')?.dirty && validateForm.get('tel')?.errors" -->
<small
[class]="{
'form-prompt': true,
'form-error':
form.get('name')?.dirty && form.get('name')?.errors
}"
>
请输入以字母开头,3到15位名称
</small>
</div>
</div>
<div class="field grid">
<label
htmlFor="tel"
class="col-12 mb-3 md:col-3 md:mb-0 text-xl justify-content-end"
>电话:<span style="color: firebrick">*</span></label
>
<div class="col-12 md:col-9">
<input
pInputText
id="tel"
type="text"
name="tel"
placeholder="电话"
formControlName="tel"
/>
</div>
</div>
<div class="field grid">
<label
htmlFor="confirmTel"
class="col-12 mb-3 md:col-3 md:mb-0 text-xl justify-content-end"
>确认电话:<span style="color: firebrick">*</span></label
>
<div class="col-12 md:col-9">
<input
pInputText
id="confirmTel"
type="text"
name="confirmTel"
placeholder="电话"
formControlName="confirmTel"
/>
</div>
</div>
<div class="field grid">
<label
htmlFor="sex"
class="col-12 mb-3 md:col-3 md:mb-0 text-xl justify-content-end"
>联系方式:<span style="color: firebrick">*</span></label
>
<div class="col-12 md:col-9" formArrayName="contacts">
<div
class="formgrid grid"
*ngFor="let contact of contacts.controls; let i = index"
[formGroupName]="i"
>
<div class="field col-4">
<input
pInputText
type="text"
name="username"
placeholder="用户"
formControlName="username"
class=""
/>
</div>
<div class="field col-4">
<input
pInputText
type="text"
name="address"
placeholder="地址"
formControlName="address"
/>
</div>
<div class="field col-3">
<input
pInputText
type="text"
name="phone"
placeholder="电话"
formControlName="phone"
/>
</div>
<div class="field col-1">
<button type="button" [disabled]="i==0" pButton pRipple class="p-button-text p-button-sm pl-0" (click)="removeContact(i)"><i class="pi pi-times"></i></button>
</div>
</div>
<div>
<button type="button" pButton pRipple class="p-button-text p-button-sm" (click)="addContact()"><i class="pi pi-plus"></i></button>
</div>
</div>
</div>
....
<div class="field grid justify-content-center">
<button type="submit" pButton pRipple>提交</button>
</div>
</div>
</form>
</div>
</div>
表单控件使用 formControlName="phone" 绑定表单值
添加表单校验
使用Validators类进行表单校验,在创建FormGroup添加表单规则
错误信息提示
使用如下获取控件状态 form.get('name')?.dirty && form.get('name')?.errors,其对应属性值来源于FormControl类,具体属性可参考
https://angular.cn/api/forms/FormControl
自定义验证器
confirmationValidator = (
control: UntypedFormControl
): { [s: string]: boolean } => {
if (!control.value) {
return { required: true }
} else if (control.value !== this.form.controls['tel'].value) {
return { confirm: true, error: true }
}
return {}
}
自定义异步验证器和同步验证器很像,只是它们必须返回一个稍后会输出 null 或“验证错误对象”的承诺(Promise)或可观察对象,如果是可观察对象,那么它必须在某个时间点被完成(complete),那时候这个表单就会使用它输出的最后一个值作为验证结果。(译注:HTTP 服务是自动完成的,但是某些自定义的可观察对象可能需要手动调用 complete 方法)
异步验证器
-
validate()函数必须返回一个 Promise 或可观察对象 -
回的可观察对象必须是有尽的,这意味着它必须在某个时刻完成(complete)。要把无尽的可观察对象转换成有尽的,可以在管道中加入过滤操作符,比如
first、last、take或takeUntil。示例
validate( control: AbstractControl ): Observable<ValidationErrors | null> { return this.heroesService.isAlterEgoTaken(control.value).pipe( map(isTaken => (isTaken ? { uniqueAlterEgo: true } : null)), catchError(() => of(null)) ); }
表单控制
//可以使用ngModalChange
<input type="text" [(ngModal)]="name" (ngModalChange)="nameChange()" />
//也可以使用如下
ngOnInt() {
this.form.get("name").valueChanges.subscribe(data => {
console.log(data);
}
}
其他
- patchValue:设置表单控件的值(可以设置全部,也可以设置其中某一个,其他不受影响)
- setValue:设置表单控件的值 (设置全部,不能排除任何一个)
- valueChanges:当表单控件的值发生变化时被触发的事件
- reset:表单内容置空