文章目录

Angular 表单:模板驱动与响应式表单

发布于 2026-04-11 13:15:25 · 浏览 6 次 · 评论 0 条

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 属性。
  • 添加 requiredminlength 属性进行基础验证。

第三阶段:响应式表单

响应式表单将表单逻辑从模板中移出,完全在组件类(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清空之前的代码,使用 formGroupformControlName 指令将 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 指令 基于 FormControlFormGroup

当你需要快速实现一个简单的登录框,且验证逻辑只是“必填”或“长度限制”时,选择模板驱动表单。当你需要处理动态增减表单项、跨字段验证(例如“密码”与“确认密码”必须一致)或复杂的表单状态管理时,选择响应式表单。

评论 (0)

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

扫一扫,手机查看

扫描上方二维码,在手机上查看本文