Angular 表单:模板驱动与响应式表单
在 Angular 开发中,处理用户输入是核心功能之一。Angular 提供了两种构建表单的主要方式:模板驱动表单和响应式表单。前者逻辑简单,适合快速开发;后者逻辑集中,适合处理复杂的交互场景。
第一阶段:准备工作
在开始编写代码之前,确保你已经安装了 Angular CLI 并创建了一个新的项目。如果你还没有项目,打开终端,运行以下命令创建一个名为 form-demo 的新项目,并生成一个名为 user-form 的组件。
运行命令:
ng new form-demo
cd form-demo
ng generate component user-form
为了在应用中使用该组件,打开 src/app/app.component.html,删除所有默认内容,添加组件选择器标签:
<app-user-form></app-user-form>
第二阶段:模板驱动表单
模板驱动表单的核心逻辑在于 HTML 模板。这种方式通过指令(如 ngModel)将数据和行为绑定在视图中,适合逻辑简单的表单。
1. 导入表单模块
打开 src/app/user-form/user-form.component.ts 文件。在文件顶部,导入 FormsModule。如果你使用的是独立组件(Standalone Component),将其添加到 @Component 装饰器的 imports 数组中。
修改代码如下:
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms'; // 导入 FormsModule
@Component({
selector: 'app-user-form',
standalone: true,
imports: [CommonModule, FormsModule], // 添加到 imports
templateUrl: './user-form.component.html',
styleUrls: ['./user-form.component.css']
})
export class UserFormComponent {
username = '';
onSubmit() {
console.log('模板驱动表单提交:', this.username);
}
}
2. 编写模板 HTML
打开 src/app/user-form/user-form.component.html 文件。创建一个表单,并使用 ngModel 指令进行双向数据绑定。注意,必须为每个输入框添加 name 属性,以便 ngModel 能够注册控件。
编写以下代码:
<h2>模板驱动表单</h2>
<form (ngSubmit)="onSubmit()" #userForm="ngForm">
<div>
<label for="username">用户名:</label>
<input
id="username"
name="username"
type="text"
[(ngModel)]="username"
required
minlength="3"
>
<!-- 简单的验证提示 -->
<div *ngIf="userForm.controls['username'].invalid && userForm.controls['username'].touched">
<small *ngIf="userForm.controls['username'].errors?.['required']">用户名必填</small>
<small *ngIf="userForm.controls['username'].errors?.['minlength']">至少3个字符</small>
</div>
</div>
<button type="submit" [disabled]="userForm.invalid">提交</button>
</form>
在上述代码中:
- 使用
#userForm="ngForm"创建了表单的引用变量,用于访问表单的状态。 - 绑定
[(ngModel)]到组件类中的username属性。 - 添加
required和minlength属性进行基础验证。
第三阶段:响应式表单
响应式表单将表单逻辑从模板中移出,完全在组件类(TypeScript)中定义。这种方式提供了更强的可预测性和可测试性,适合复杂场景。
1. 导入响应式表单模块
打开 src/app/user-form/user-form.component.ts。导入 ReactiveFormsModule 代替 FormsModule,并导入 FormControl, FormGroup, Validators。
修改代码如下:
import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule, FormControl, FormGroup, Validators } from '@angular/forms'; // 导入响应式模块
@Component({
selector: 'app-user-form',
standalone: true,
imports: [CommonModule, ReactiveFormsModule], // 添加 ReactiveFormsModule
templateUrl: './user-form.component.html',
styleUrls: ['./user-form.component.css']
})
export class UserFormComponent implements OnInit {
loginForm!: FormGroup; // 定义表单模型
ngOnInit() {
// 在初始化时构建表单结构
this.loginForm = new FormGroup({
username: new FormControl('', [
Validators.required,
Validators.minLength(3)
]),
email: new FormControl('', [
Validators.required,
Validators.email
])
});
}
onSubmit() {
if (this.loginForm.valid) {
console.log('响应式表单提交值:', this.loginForm.value);
} else {
this.markFormGroupTouched(this.loginForm);
}
}
// 辅助方法:手动触发验证显示
private markFormGroupTouched(formGroup: FormGroup) {
Object.keys(formGroup.controls).forEach(key => {
const control = formGroup.get(key);
control?.markAsTouched();
});
}
}
2. 绑定模板到模型
打开 src/app/user-form/user-form.component.html。清空之前的代码,使用 formGroup、formControlName 指令将 HTML 元素连接到组件类中定义的模型。
编写以下代码:
<h2>响应式表单</h2>
<form [formGroup]="loginForm" (ngSubmit)="onSubmit()">
<div>
<label for="react-username">用户名:</label>
<input
id="react-username"
type="text"
formControlName="username"
>
<!-- 通过 getter 或模板访问控制权 -->
<div *ngIf="loginForm.get('username')?.invalid && loginForm.get('username')?.touched">
<small *ngIf="loginForm.get('username')?.errors?.['required']">用户名必填</small>
<small *ngIf="loginForm.get('username')?.errors?.['minlength']">至少3个字符</small>
</div>
</div>
<div>
<label for="react-email">邮箱:</label>
<input
id="react-email"
type="email"
formControlName="email"
>
<div *ngIf="loginForm.get('email')?.invalid && loginForm.get('email')?.touched">
<small *ngIf="loginForm.get('email')?.errors?.['required']">邮箱必填</small>
<small *ngIf="loginForm.get('email')?.errors?.['email']">邮箱格式不正确</small>
</div>
</div>
<button type="submit" [disabled]="loginForm.invalid">提交</button>
</form>
在上述代码中:
- 绑定
[formGroup]="loginForm"将 HTML 表单与 TypeScript 中的对象关联。 - 使用
formControlName="username"将输入框与表单模型中的特定控件匹配。 - 移除 了
[(ngModel)],因为数据流完全由loginForm对象管理。
第四阶段:两种方式的对比
为了在项目中做出正确的选择,参考下表对比两种表单模式的差异。
| 特性 | 模板驱动表单 | 响应式表单 |
|---|---|---|
| 数据流向 | 双向数据绑定,指令驱动 | 单向数据流,模型驱动 |
| 代码编写位置 | 主要在 HTML 模板中 | 主要在 TypeScript 组件类中 |
| 适用场景 | 简单表单、原型开发 | 复杂表单、动态验证、自定义验证器 |
| 可测试性 | 较难进行单元测试,依赖 DOM | 易于单元测试,纯逻辑代码 |
| 验证逻辑 | 分散在 HTML 属性中 | 集中在组件类的 Validators 中 |
| 底层基础 | 基于 ngModel 指令 |
基于 FormControl 和 FormGroup |
当你需要快速实现一个简单的登录框,且验证逻辑只是“必填”或“长度限制”时,选择模板驱动表单。当你需要处理动态增减表单项、跨字段验证(例如“密码”与“确认密码”必须一致)或复杂的表单状态管理时,选择响应式表单。

暂无评论,快来抢沙发吧!