简介
Angular 提供了两种处理表单的方式:模板驱动表单 和 响应式表单 (也称为 模型驱动表单)。模板驱动表单是在 Angular 中处理表单的默认方式。使用模板驱动表单时,模板指令被用来构建表单的内部表示。而使用响应式表单时,你需要在组件类中构建自己的表单表示。
以下是响应式表单的一些优势:
- 使用自定义验证器
- 动态改变验证规则
- 动态添加表单字段
在本文中,你将探索如何将响应式表单应用到一个示例 Angular 应用程序中。
先决条件
如果你想跟着本文操作,你需要:
- 本地安装 Node.js,你可以按照《如何安装 Node.js 并创建本地开发环境》进行操作。
本文假设你具有一些 Angular 的基础知识。
本文还假设你是在使用 @angular/cli
生成的全新 Angular 项目进行构建。如果你刚开始使用 Angular CLI,你可以参考本文。
本教程经过 Node v15.1.0、npm
v6.14.8、@angular/core
v11.0.0 和 @angular/forms
v11.0.0 的验证。
步骤 1 ------ 设置项目
为了本教程的目的,你将从使用 @angular/cli
生成的默认 Angular 项目开始构建。
command
npx @angular/cli new angular-reactive-forms-example --style=css --routing=false --skip-tests
这将配置一个新的 Angular 项目,其中样式设置为 "CSS"(而不是 "Sass"、"Less" 或 "Stylus"),没有路由,并且跳过了测试。
进入新创建的项目目录:
command
cd angular-reactive-forms-example
为了使用响应式表单,你将使用 ReactiveFormsModule
而不是 FormsModule
。
在你的代码编辑器中打开 app.module.ts
并添加 ReactiveFormsModule
:
typescript
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 { }
到此为止,你应该已经有了一个使用 ReactiveFormsModule
的新的 Angular 项目。
步骤 2 ------ 向组件模板添加表单
使用响应式表单时,逻辑完全在组件类中声明。
在你的代码编辑器中打开 app.component.html
并添加以下代码:
html
<form [formGroup]="myForm" (ngSubmit)="onSubmit(myForm)">
<div>
<label>
Name:
<input formControlName="name" placeholder="Your name">
</label>
</div>
<div>
<label>
Email:
<input formControlName="email" placeholder="Your email">
</label>
</div>
<div>
<label>
Message:
<input formControlName="message" placeholder="Your message">
</label>
</div>
<button type="submit">Send</button>
</form>
这段代码将创建一个包含三个字段(name
、email
和 message
)的表单。还将有一个标签为 "Send" 的 "submit"
按钮。在提交表单时,将调用 onSubmit(myForm)
方法。
让我们来分解一下:
formGroup
:表单将在组件类中被视为FormGroup
,因此formGroup
指令允许给表单组指定一个名称。ngSubmit
:这是在提交表单时触发的事件。formControlName
:每个表单字段应该有一个formControlName
指令,其值将在组件类中使用。
到此为止,你应该已经有了一个使用表单的组件模板。
步骤 3 ------ 构建组件类
接下来,在组件类中,你将定义 FormGroup
和 FormGroup
内的各个 FormControl
。
如果在 newing FormControl
时提供了一个值,它将被用作字段的初始值。
注意 FormGroup
和 FormControl
的名称与模板中使用的名称相同。还要注意你如何在 ngOnInit
生命周期钩子中初始化 FormGroup
:
typescript
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
方法实际上并没有将提交的表单值传递给任何外部服务或服务器。它用于展示你如何访问表单的有效性和 FormControl
的值。
到此为止,你可以编译你的应用程序并在 web 浏览器中打开它。在为 name
、email
和 message
输入值并点击 Submit 后,控制台日志将显示这些值。
第四步 --- 更新组件类以使用 FormBuilder
ngOnInit
中的表单构建可以使用 FormBuilder
辅助程序进行重写。这样可以避免对表单组和表单控件进行 newing。
在代码编辑器中打开 app.component.ts
,移除 FormControl
并用 FormBuilder
替换 FormGroup
:
typescript
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); // true or false
console.log('Name', form.value.name);
console.log('Email', form.value.email);
console.log('Message', form.value.message);
}
}
使用 FormBuilder
的这段代码减少了创建 FormGroup
的样板代码。
第五步 --- 更新组件类以使用 Validators
将 Validators
类添加到你的导入中,并使用数组而不是简单的字符串值来声明表单控件。
数组中的第一个值是初始表单值,第二个值是要使用的验证器。请注意,可以通过将它们包装成数组来在同一个表单控件上使用多个验证器。
在代码编辑器中打开 app.component.ts
,添加 Validators
:
typescript
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); // true or false
console.log('Name', form.value.name);
console.log('Email', form.value.email);
console.log('Message', form.value.message);
}
}
这段代码为 name
、email
和 message
字段添加了 required
。它还确保 email
值使用有效电子邮件地址的格式。它还确保 message
值至少为 15 个字符长。
如果这些表单要求中的任何一个未通过,valid
值将为 false
。如果所有这些表单要求都通过,valid
值将为 true
。
第六步 --- 在模板中访问表单值和有效性
在模板中,可以访问每个 FormControl
的值和有效性,以及整个表单组的值和有效性。
打开 app.component.html
,使用 *ngIf
来显示反馈消息,告诉用户表单值无效:
html
<form [formGroup]="myForm" (ngSubmit)="onSubmit(myForm)">
<div>
<label>
Name:
<input formControlName="name" placeholder="Your name">
</label>
<div *ngIf="myForm.get('name').invalid && (myForm.get('name').dirty || myForm.get('name').touched)">
Please provide a name.
</div>
</div>
<div>
<label>
Email:
<input formControlName="email" placeholder="Your email">
</label>
<div *ngIf="myForm.get('email').invalid && (myForm.get('email').dirty || myForm.get('email').touched)">
Please provide a valid email address.
</div>
</div>
<div>
<label>
Message:
<input formControlName="message" placeholder="Your message">
</label>
<div *ngIf="myForm.get('message').invalid && (myForm.get('message').dirty || myForm.get('message').touched)">
Messages must be at least 15 characters long.
</div>
</div>
<button type="submit" [disabled]="myForm.invalid">Send</button>
</form>
这段代码检查用户是否与字段交互(dirty
或 touched
)。然后,如果值未通过验证要求,它将显示错误消息。Send 按钮也将在解决表单值的所有问题之前被禁用。
有多种方法可以检索表单控件的值。此示例使用 myForm.get('name')
,它等同于 myForm.controls.name
。可以使用 .hasError('required')
或 .errors.required
检索错误信息。
结论
在本文中,您探讨了如何将响应式表单应用于一个示例 Angular 应用程序。您使用了 FormControl
、FormGroup
、FormBuilder
和 Validators
来构建一个带有验证的示例表单。如需了解更多功能,请参考官方文档。
如果您想了解更多关于 Angular 的知识,请查看我们的 Angular 专题页面,了解相关练习和编程项目。