模板驱动表单是Angular中两种表单处理方式之一(另一种是响应式表单)。它通过在模板中使用指令来构建表单,适合简单的表单场景。
FormsModule
创建一个简单的用户注册表单:
<!-- app.component.html -->
<form #userForm="ngForm" (ngSubmit)="onSubmit(userForm)">
<div class="form-group">
<label for="name">用户名</label>
<input
type="text"
id="name"
name="name"
[(ngModel)]="user.name"
#name="ngModel"
required
minlength="3"
class="form-control">
<div *ngIf="name.invalid && (name.dirty || name.touched)" class="error-msg">
<div *ngIf="name.errors?.['required']">用户名是必填项</div>
<div *ngIf="name.errors?.['minlength']">用户名至少3个字符</div>
</div>
</div>
<div class="form-group">
<label for="email">邮箱</label>
<input
type="email"
id="email"
name="email"
[(ngModel)]="user.email"
#email="ngModel"
required
email
class="form-control">
<div *ngIf="email.invalid && (email.dirty || email.touched)" class="error-msg">
<div *ngIf="email.errors?.['required']">邮箱是必填项</div>
<div *ngIf="email.errors?.['email']">请输入有效的邮箱地址</div>
</div>
</div>
<div class="form-group">
<label for="password">密码</label>
<input
type="password"
id="password"
name="password"
[(ngModel)]="user.password"
#password="ngModel"
required
pattern="^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$"
class="form-control">
<div *ngIf="password.invalid && (password.dirty || password.touched)" class="error-msg">
<div *ngIf="password.errors?.['required']">密码是必填项</div>
<div *ngIf="password.errors?.['pattern']">密码必须至少8个字符,包含字母和数字</div>
</div>
</div>
<button type="submit" [disabled]="userForm.invalid" class="btn btn-primary">
注册
</button>
</form>
// app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
user = {
name: '',
email: '',
password: ''
};
onSubmit(form: any) {
if (form.valid) {
console.log('表单数据:', this.user);
console.log('表单值:', form.value);
// 这里可以发送数据到服务器
alert('注册成功!');
form.reset();
}
}
}
Angular提供的内置验证器:
<!-- 内置验证器示例 -->
<input type="text" [(ngModel)]="model.value" name="field" required>
<input type="email" [(ngModel)]="model.email" name="email" email>
<input type="number" [(ngModel)]="model.age" name="age" min="18" max="100">
<input type="text" [(ngModel)]="model.username" name="username" minlength="3" maxlength="20">
<input type="text" [(ngModel)]="model.zipcode" name="zipcode" pattern="[0-9]{5}">
<!-- 单选按钮组 -->
<div class="form-group">
<label>性别</label>
<div class="form-check">
<input
type="radio"
id="male"
name="gender"
[(ngModel)]="user.gender"
value="male"
class="form-check-input">
<label for="male" class="form-check-label">男</label>
</div>
<div class="form-check">
<input
type="radio"
id="female"
name="gender"
[(ngModel)]="user.gender"
value="female"
class="form-check-input">
<label for="female" class="form-check-label">女</label>
</div>
</div>
<!-- 复选框 -->
<div class="form-group">
<div class="form-check">
<input
type="checkbox"
id="subscribe"
name="subscribe"
[(ngModel)]="user.subscribe"
class="form-check-input">
<label for="subscribe" class="form-check-label">订阅新闻邮件</label>
</div>
</div>
<!-- 多选框组 -->
<div class="form-group">
<label>兴趣爱好</label>
<div class="form-check" *ngFor="let hobby of hobbies">
<input
type="checkbox"
[id]="hobby.id"
[value]="hobby.value"
[checked]="user.hobbies?.includes(hobby.value)"
(change)="onHobbyChange($event, hobby.value)"
class="form-check-input">
<label [for]="hobby.id" class="form-check-label">{{hobby.label}}</label>
</div>
</div>
// 组件中的处理逻辑
hobbies = [
{ id: 'sports', value: 'sports', label: '运动' },
{ id: 'reading', value: 'reading', label: '阅读' },
{ id: 'music', value: 'music', label: '音乐' }
];
user = {
gender: 'male',
subscribe: true,
hobbies: ['sports', 'music']
};
onHobbyChange(event: any, hobbyValue: string) {
if (event.target.checked) {
if (!this.user.hobbies.includes(hobbyValue)) {
this.user.hobbies.push(hobbyValue);
}
} else {
const index = this.user.hobbies.indexOf(hobbyValue);
if (index > -1) {
this.user.hobbies.splice(index, 1);
}
}
}
<!-- 基本下拉框 -->
<div class="form-group">
<label for="country">国家</label>
<select
id="country"
name="country"
[(ngModel)]="user.country"
class="form-control">
<option value="">请选择国家</option>
<option *ngFor="let country of countries" [value]="country.code">
{{country.name}}
</option>
</select>
</div>
<!-- 多选下拉框 -->
<div class="form-group">
<label for="languages">掌握的语言</label>
<select
id="languages"
name="languages"
[(ngModel)]="user.languages"
multiple
class="form-control">
<option *ngFor="let lang of languages" [value]="lang">
{{lang}}
</option>
</select>
</div>
Angular会自动为表单控件添加CSS类,反映其状态:
| 状态 | 为真时 | 为假时 |
|---|---|---|
ng-valid |
控件通过验证 | 控件未通过验证 |
ng-invalid |
控件未通过验证 | 控件通过验证 |
ng-pristine |
控件值未改变 | 控件值已改变 |
ng-dirty |
控件值已改变 | 控件值未改变 |
ng-touched |
控件已被访问过 | 控件未被访问 |
ng-untouched |
控件未被访问 | 控件已被访问过 |
/* 自定义验证样式 */
input.ng-valid {
border-color: #28a745;
}
input.ng-invalid.ng-touched {
border-color: #dc3545;
}
input.ng-pending {
border-color: #ffc107;
}
.form-submitted input.ng-invalid {
border-color: #dc3545;
}
| 特性 | 模板驱动表单 | 响应式表单 |
|---|---|---|
| 设置方式 | 在模板中通过指令 | 在组件类中创建 |
| 数据模型 | 非显式,自动创建 | 显式,不可变对象 |
| 可预测性 | 异步 | 同步 |
| 表单验证 | 指令 | 函数 |
| 动态性 | 有限 | 高 |
| 适用场景 | 简单表单、快速原型 | 复杂表单、动态表单 |