案例演示

一、前言
Form 表单组件是 Web 应用中最常见的交互方式,几乎每个应用都需要处理用户输入。然而,很多开发者在使用表单时容易陷入坑点,如验证逻辑混乱、数据绑定失效、性能低下等。本文通过 DevUI Form 的实战案例,深入讲解表单组件的深度用法和常见避坑技巧。
二、核心概念:dForm 组件结构
2.1 表单基础结构
DevUI Form 由三个核心组件组成:dForm、d-form-item、d-form-control。
typescript
import { Component, OnInit } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { ButtonModule } from 'ng-devui/button';
import { TextInputModule } from 'ng-devui/text-input';
import { CheckBoxModule } from 'ng-devui/checkbox';
import { SelectModule } from 'ng-devui/select';
import { RadioModule } from 'ng-devui/radio';
import { DatepickerModule } from 'ng-devui/datepicker';
import { FormModule } from 'ng-devui/form';
import { TooltipModule } from 'ng-devui/tooltip';
import { TagsInputModule } from 'ng-devui/tags-input';
import { ToggleModule } from 'ng-devui/toggle';
import { TextareaModule } from 'ng-devui/textarea';
@Component({
selector: 'app-root',
standalone: true,
imports: [
FormsModule,
CommonModule,
ButtonModule,
TextInputModule,
CheckBoxModule,
SelectModule,
RadioModule,
DatepickerModule,
FormModule,
TooltipModule,
TagsInputModule,
ToggleModule,
TextareaModule
],
templateUrl: './app.component.html',
styleUrl: './app.component.css'
})
export class AppComponent implements OnInit {
ngOnInit(): void {
// 初始化
}
}
说明 :导入所有必需的 DevUI 模块。FormModule 提供 dForm、d-form-item、d-form-control、d-form-operation 等组件。FormsModule 用于支持 [(ngModel)] 双向绑定。
2.2 表单数据结构
typescript
// 表单数据
formData = {
username: '',
email: '',
password: '',
confirmPassword: '',
department: '',
position: '',
joinDate: '',
description: '',
agreeTerms: false,
newsletter: false,
gender: ''
};
说明:定义一个对象来存储所有表单字段的值。这种方式比逐个声明变量更清晰,便于管理和序列化。
2.3 表单选项数据
typescript
// 表单选项
departments = [
{ id: 1, label: '技术部' },
{ id: 2, label: '产品部' },
{ id: 3, label: '设计部' },
{ id: 4, label: '市场部' },
{ id: 5, label: '人事部' },
{ id: 6, label: '财务部' }
];
positions = [
{ id: 1, label: '工程师' },
{ id: 2, label: '高级工程师' },
{ id: 3, label: '技术经理' },
{ id: 4, label: '产品经理' },
{ id: 5, label: '设计师' },
{ id: 6, label: '总监' }
];
genderOptions = [
{ id: 1, label: '男' },
{ id: 2, label: '女' },
{ id: 3, label: '其他' }
];
tagList = [
{ id: 1, label: 'Angular' },
{ id: 2, label: 'TypeScript' },
{ id: 3, label: 'DevUI' },
{ id: 4, label: 'Web' },
{ id: 5, label: 'Frontend' }
];
说明 :为下拉选择、单选按钮、标签等组件提供选项数据。使用 { id, label } 结构,其中 label 用于显示,id 用于唯一标识。
三、表单模板结构
3.1 基础表单框架
html
<form dForm ngForm (ngSubmit)="onSubmit()" class="register-form">
<!-- 表单项 -->
<d-form-item>
<d-form-label [required]="true">用户名</d-form-label>
<d-form-control>
<input dTextInput name="username" [(ngModel)]="formData.username" placeholder="请输入用户名(至少3个字符)" />
</d-form-control>
</d-form-item>
<!-- 按钮 -->
<d-form-operation>
<d-button bsStyle="primary" type="submit" style="margin-right: 8px;">提交</d-button>
<d-button bsStyle="common" type="button" (click)="resetForm()">重置</d-button>
</d-form-operation>
</form>
说明 :dForm 是表单容器,ngForm 启用模板驱动表单。d-form-item 包装每个表单字段,d-form-label 显示标签,d-form-control 包含实际的输入控件。d-form-operation 用于放置表单按钮。
3.2 文本输入字段
html
<d-form-item>
<d-form-label [required]="true">邮箱</d-form-label>
<d-form-control>
<input dTextInput type="email" name="email" [(ngModel)]="formData.email" placeholder="请输入邮箱地址" />
</d-form-control>
</d-form-item>
说明 :使用 dTextInput 指令为原生 <input> 元素应用 DevUI 样式。[(ngModel)] 实现双向数据绑定,用户输入自动更新 formData.email。
3.3 密码字段
html
<d-form-item>
<d-form-label [required]="true">密码</d-form-label>
<d-form-control>
<input dTextInput type="password" name="password" [(ngModel)]="formData.password" placeholder="请输入密码(至少6个字符)" />
</d-form-control>
</d-form-item>
<d-form-item>
<d-form-label [required]="true">确认密码</d-form-label>
<d-form-control>
<input dTextInput type="password" name="confirmPassword" [(ngModel)]="formData.confirmPassword" placeholder="请再次输入密码" />
</d-form-control>
</d-form-item>
说明:密码字段需要两个输入框,一个用于输入,一个用于确认。这样可以在提交前验证两次输入是否一致。
3.4 单选按钮组
html
<d-form-item>
<d-form-label [required]="true">性别</d-form-label>
<d-form-control>
<d-radio-group name="gender" [direction]="'row'" [(ngModel)]="formData.gender">
<d-radio *ngFor="let option of genderOptions" [value]="option.label">
{{ option.label }}
</d-radio>
</d-radio-group>
</d-form-control>
</d-form-item>
说明 :d-radio-group 用于包装多个 d-radio 组件。[direction]="'row'" 使单选按钮横向排列。[(ngModel)] 绑定到 formData.gender,选中的值自动更新。
3.5 下拉选择框
html
<d-form-item>
<d-form-label [required]="true">部门</d-form-label>
<d-form-control>
<d-select
name="department"
[options]="departments"
[filterKey]="'label'"
[(ngModel)]="formData.department"
placeholder="请选择部门">
</d-select>
</d-form-control>
</d-form-item>
说明 :d-select 组件用于下拉选择。[options] 绑定选项数组,[filterKey]="'label'" 指定显示的字段。[(ngModel)] 绑定选中的值。
3.6 日期选择
html
<d-form-item>
<d-form-label [required]="true">入职日期</d-form-label>
<d-form-control>
<input dTextInput type="date" name="joinDate" [(ngModel)]="formData.joinDate" />
</d-form-control>
</d-form-item>
说明 :使用原生 type="date" 输入框。浏览器会提供日期选择器。[(ngModel)] 绑定日期值。
3.7 文本域
html
<d-form-item>
<d-form-label>描述</d-form-label>
<d-form-control>
<textarea dTextarea name="description" [(ngModel)]="formData.description" placeholder="请输入描述信息" maxlength="200" style="height: 80px"></textarea>
</d-form-control>
</d-form-item>
说明 :dTextarea 指令为 <textarea> 元素应用 DevUI 样式。maxlength="200" 限制输入长度。style="height: 80px" 设置高度。
3.8 标签输入
html
<d-form-item>
<d-form-label>技能标签</d-form-label>
<d-form-control>
<d-tags-input
name="tags"
(click)="$event.stopPropagation()"
[displayProperty]="'label'"
[tags]="addedTags"
[placeholder]="'输入技能标签'"
[suggestionList]="tagList">
</d-tags-input>
</d-form-control>
</d-form-item>
说明 :d-tags-input 用于输入多个标签。[displayProperty]="'label'" 指定显示的字段。[suggestionList]="tagList" 提供建议列表。(click)="$event.stopPropagation()" 防止事件冒泡。
3.9 开关控件
html
<d-form-item>
<d-form-label>订阅通讯</d-form-label>
<d-form-control>
<d-toggle name="newsletter" [(ngModel)]="formData.newsletter"></d-toggle>
</d-form-control>
</d-form-item>
说明 :d-toggle 是开关控件,用于布尔值。[(ngModel)] 绑定到 formData.newsletter,开关状态自动更新。
3.10 复选框
html
<d-form-item>
<d-form-label [required]="true">服务条款</d-form-label>
<d-form-control>
<d-checkbox name="agreeTerms" [(ngModel)]="formData.agreeTerms">
我已阅读并同意服务条款
</d-checkbox>
</d-form-control>
</d-form-item>
说明 :d-checkbox 用于单个复选框。[(ngModel)] 绑定到布尔值。用户勾选时自动更新。
四、表单验证
4.1 完整的验证逻辑
typescript
// 验证表单
validateForm(): boolean {
if (!this.formData.username || this.formData.username.length < 3) {
console.log('用户名必填且至少3个字符');
return false;
}
if (!this.formData.email || !this.isValidEmail(this.formData.email)) {
console.log('邮箱格式不正确');
return false;
}
if (!this.formData.password || this.formData.password.length < 6) {
console.log('密码必填且至少6个字符');
return false;
}
if (this.formData.password !== this.formData.confirmPassword) {
console.log('两次输入的密码不一致');
return false;
}
if (!this.formData.department) {
console.log('请选择部门');
return false;
}
if (!this.formData.position) {
console.log('请选择职位');
return false;
}
if (!this.formData.gender) {
console.log('请选择性别');
return false;
}
if (!this.formData.agreeTerms) {
console.log('必须同意服务条款');
return false;
}
return true;
}
说明 :逐个验证每个字段。使用 console.log 记录验证失败的原因。这种方式清晰易懂,便于调试。关键是要验证必填字段、长度限制、格式要求和跨字段验证(如密码匹配)。
4.2 邮箱验证
typescript
// 邮箱验证
isValidEmail(email: string): boolean {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
说明 :使用正则表达式验证邮箱格式。这个正则表达式检查邮箱是否包含 @ 和 .,是一个基础的验证方式。生产环境可以使用更复杂的正则或调用后端 API 验证。
五、表单提交和重置
5.1 表单提交
typescript
onSubmit(): void {
// 验证表单
if (!this.validateForm()) {
console.log('表单验证失败');
return;
}
const submitData = { ...this.formData };
this.submittedForms.push(submitData);
console.log('表单提交成功:', submitData);
// 重置表单
this.resetForm();
}
说明 :先验证表单,验证失败则返回。验证通过后,复制 formData 到 submitData(使用扩展运算符创建副本),然后保存到历史记录。最后调用 resetForm() 清空表单。
5.2 表单重置
typescript
// 重置表单
resetForm(): void {
this.formData = {
username: '',
email: '',
password: '',
confirmPassword: '',
department: '',
position: '',
joinDate: '',
description: '',
agreeTerms: false,
newsletter: false,
gender: ''
};
this.addedTags = [];
}
说明 :重置表单时,需要将所有字段恢复到初始值。字符串字段设为空字符串,布尔字段设为 false,数组字段设为空数组。这样用户可以继续填写新的表单。
六、提交历史管理
6.1 保存提交历史
typescript
// 表单提交历史
submittedForms: any[] = [];
onSubmit(): void {
if (!this.validateForm()) {
console.log('表单验证失败');
return;
}
const submitData = { ...this.formData };
this.submittedForms.push(submitData);
console.log('表单提交成功:', submitData);
this.resetForm();
}
说明 :使用数组 submittedForms 保存所有提交的表单数据。每次提交时,使用 push() 添加新的记录。这样用户可以查看历史提交记录。
6.2 显示提交历史
html
<!-- 提交历史 -->
<div class="history-section" *ngIf="submittedForms.length > 0">
<div class="history-header">
<h2>提交历史 ({{ submittedForms.length }})</h2>
<d-button bsStyle="common" (click)="clearHistory()">清空历史</d-button>
</div>
<div class="history-list">
<div class="history-item" *ngFor="let form of submittedForms; let i = index">
<div class="history-content">
<p><strong>用户名:</strong> {{ form.username }}</p>
<p><strong>邮箱:</strong> {{ form.email }}</p>
<p><strong>部门:</strong> {{ form.department?.label }}</p>
<p><strong>职位:</strong> {{ form.position?.label }}</p>
<p><strong>性别:</strong> {{ form.gender }}</p>
<p><strong>入职日期:</strong> {{ form.joinDate }}</p>
</div>
<d-button bsStyle="text" class="delete-btn" (click)="deleteHistory(i)">删除</d-button>
</div>
</div>
</div>
说明 :使用 *ngIf="submittedForms.length > 0" 条件渲染历史记录。使用 *ngFor 遍历历史记录。注意使用 form.department?.label 安全访问对象属性,避免 undefined 错误。
6.3 清空和删除历史
typescript
// 清空提交历史
clearHistory(): void {
this.submittedForms = [];
}
// 删除单条历史记录
deleteHistory(index: number): void {
this.submittedForms.splice(index, 1);
}
说明 :clearHistory() 清空所有历史记录。deleteHistory(index) 删除指定索引的记录。使用 splice() 方法从数组中移除元素。
七、常见坑点和避坑技巧
| 坑点 | 原因 | 解决方案 |
|---|---|---|
| 数据绑定失效 | 忘记使用 [(ngModel)] |
确保每个输入字段都有 [(ngModel)]="formData.fieldName" |
| 验证逻辑混乱 | 验证规则分散在各处 | 集中在 validateForm() 方法中 |
| 密码不匹配验证缺失 | 只验证单个字段 | 添加跨字段验证:password === confirmPassword |
| 表单重置不完全 | 只清空部分字段 | 重置时恢复所有字段到初始值 |
| 选择框显示错误 | 没有指定 filterKey |
使用 [filterKey]="'label'" 指定显示字段 |
| 标签输入无法工作 | 缺少 suggestionList |
提供 [suggestionList] 和 [displayProperty] |
| 表单提交后页面卡顿 | 没有重置表单 | 提交后调用 resetForm() |
| 历史记录内存泄漏 | 无限增长的数组 | 提供清空历史的功能 |
八、总结
DevUI Form 组件的深度使用需要关注以下几点:
- 正确的数据结构 - 使用对象管理表单数据,而不是逐个变量
- 完整的验证逻辑 - 集中验证,覆盖必填、格式、长度、跨字段等规则
- 双向数据绑定 - 使用
[(ngModel)]实现自动同步 - 表单生命周期 - 提交后重置,历史记录管理
- 用户体验 - 提供清晰的错误提示,支持历史查看和删除
掌握这些技巧,你就能开发出高效、易用的表单,提升用户体验和开发效率。
相关资源: