简介
说到 Angular 里的表单处理,其实有两条路可以走:模板驱动表单和响应式表单(也叫模型驱动表单)。模板驱动表单是 Angular 默认的方式,它的逻辑主要在模板里,通过指令来构建表单的内部表示。而响应式表单呢,则让你在组件类里自己构建和管理表单模型,逻辑更集中,也更灵活。
那响应式表单到底好在哪?简单来说,它天生就支持自定义验证器、动态改变验证规则、以及动态添加表单字段这些高级操作。这篇文章会带你一步步在一个示例 Angular 应用中搞清楚响应式表单怎么用。
先决条件
想要跟着动手?先确认一下环境:本地需要安装好 Node.js,可以参考“如何安装 Node.js 并创建本地开发环境”来准备。这篇文章假设你已经有了一些 Angular 基础,并且是在一个用 @angular/cli 生成的全新 Angular 项目上继续改造。如果你还不知道怎么用 CLI 创建项目,不妨先看看相关文章。
本次教程的环境是:Node v15.1.0、npm v6.14.8、@angular/core v11.0.0 和 @angular/forms v11.0.0,版本对得上,操作会更顺畅。
步骤 1 — 设置项目
直接从默认的 Angular 项目入手。用这条命令新建一个项目:
npx @angular/cli new angular-reactive-forms-example --style=css --routing=false --skip-tests
这会生成一个样式用 CSS、没有路由、跳过测试的新项目。创建完后,进入项目目录:
cd angular-reactive-forms-example
要把表单玩法从模板驱动切换到响应式,关键一步是在模块里导入 ReactiveFormsModule 而不是 FormsModule。打开 app.module.ts,加上它:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
@NgModule({
declarations: [ AppComponent ],
imports: [ BrowserModule, ReactiveFormsModule ],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
这一步做完,项目就具备了使用响应式表单的“基础设施”。
步骤 2 — 向组件模板添加表单
响应式表单的逻辑声明都在组件类里干,模板只负责绑定。打开 app.component.html,贴上这段模板代码:
这段代码创建了三个表单字段:姓名、邮箱、留言,还有一个“Send”按钮。提交时,会触发 onSubmit(myForm) 方法。几个关键点值得留意:
formGroup:把整个表单在组件类里当作FormGroup来管理,这个指令就是给表单组起个名字。ngSubmit:表单提交事件。formControlName:每个输入字段都必须配上这个指令,它的值对应组件类里定义的控件名。
模板这边就绪了。
步骤 3 — 构建组件类
现在到组件类里定义 FormGroup 和里面的各个 FormControl。初始化 FormControl 时如果传一个值进去,那就是字段的默认值。
注意模板里用的名字,和组件类里定义的名字必须完全一致。生命周期钩子 ngOnInit 是初始化 FormGroup 的好地方:
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup } 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({
name: new FormControl('Sammy'),
email: new FormControl(''),
message: new FormControl('')
});
}
onSubmit(form: FormGroup) {
console.log('Valid?', form.valid); // true or false
console.log('Name', form.value.name);
console.log('Email', form.value.email);
console.log('Message', form.value.message);
}
}
这里的 onSubmit 没有真的把数据发给服务器,只是演示怎么拿到表单的有效性(valid)和各个控件的值(form.value)。编译运行,填好表单点“Submit”,控制台就会打印出这些信息。
第四步 — 更新组件类以使用 FormBuilder
每次都手动 new FormGroup 和 new FormControl 是不是有点烦?FormBuilder 就是来简化这事的。打开 app.component.ts,移除 FormControl 的引入,换上 FormBuilder:
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } 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({
name: 'Sammy',
email: '',
message: ''
});
}
onSubmit(form: FormGroup) {
console.log('Valid?', form.valid);
console.log('Name', form.value.name);
console.log('Email', form.value.email);
console.log('Message', form.value.message);
}
}
看到没?用 FormBuilder 的 .group() 方法,直接传一个对象,键是控件名,值是默认值,代码清爽多了。
第五步 — 更新组件类以使用 Validators
表单不能光有值,还得有验证规则。把 Validators 类引进来,用数组来代替简单的字符串默认值——数组的第一个元素是初始值,第二个是验证器(可以是单个,也可以是数组里塞多个)。
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({
name: ['Sammy', Validators.required],
email: ['', [Validators.required, Validators.email]],
message: ['', [Validators.required, Validators.minLength(15)]],
});
}
onSubmit(form: FormGroup) {
console.log('Valid?', form.valid);
console.log('Name', form.value.name);
console.log('Email', form.value.email);
console.log('Message', form.value.message);
}
}
这下,name 是必填,email 既要必填又得是合法邮箱格式,message 必须至少15个字符。只要任意一项不满足,form.valid 就会是 false。
第六步 — 在模板中访问表单值和有效性
光定义验证规则还不够,得在界面上给用户反馈。打开 app.component.html,利用 *ngIf 来显示错误提示:
这段代码的逻辑很清晰:只有当用户点过或改过某个字段(也就是 dirty 或 touched 为 true),并且验证不通过时,才显示对应的错误。同时,只要整个表单还有一个地方不合法,提交按钮就一直保持禁用状态。
获取表单控件值的方式,这里用了 myForm.get('name'),它和 myForm.controls.name 是等价的。要检查具体错误类型,可以用 .hasError('required') 或 .errors.required。
结论
到这里,你已经走通了一个 Angular 响应式表单的完整流程。从最初的 FormControl 和 FormGroup,到用 FormBuilder 简化定义,再到加入 Validators 给表单套上“紧箍咒”,最后在模板里完美呈现验证反馈——这套组合拳打下来,响应式表单的核心用法应该就心里有数了。更多的进阶功能,比如自定义验证器或动态表单字段,可以再去翻翻官方文档挖一挖。
