华为云的DevUI&Form组件实战:个人信息编辑表单完整实现

最近在做项目时用到了华为云的 DevUI 组件库,其中 Form 表单组件用得比较多。踩了不少坑,也总结了一些经验,分享给大家。

前言

表单是 Web 开发中最常见的交互组件,几乎每个后台管理系统都离不开它。我在使用 DevUI 的 Form 组件时,发现它的文档虽然全面,但实际项目中会遇到一些细节问题。这篇文章就是基于一个真实的个人信息编辑表单案例,讲讲怎么用 DevUI Form 组件,以及一些容易踩的坑。

一、项目环境准备

首先,确保你的项目已经安装了 ng-devui。如果还没装,可以看我上一篇的文章:
华为云 DevUI初体验:如何快速入门项目搭建

二、组件导入和基础结构

DevUI 的 Form 组件是基于 Angular Reactive Forms 的,所以需要导入 ReactiveFormsModule。看下我的组件导入:

typescript 复制代码
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { FormModule } from 'ng-devui/form';
import { TextInputModule } from 'ng-devui/text-input';
import { SelectModule } from 'ng-devui/select';
import { DatepickerModule } from 'ng-devui/datepicker';
import { RadioModule } from 'ng-devui/radio';
import { TextareaModule } from 'ng-devui/textarea';
import { ButtonModule } from 'ng-devui/button';

@Component({
  selector: 'app-form',
  standalone: true,
  imports: [
    CommonModule,
    ReactiveFormsModule,
    FormModule,
    TextInputModule,
    SelectModule,
    DatepickerModule,
    RadioModule,
    TextareaModule,
    ButtonModule
  ],
  templateUrl: './form.component.html',
  styleUrl: './form.component.scss'
})
export class FormComponent implements OnInit {
  // ...
}

注意几个点:

  • FormModule 提供了 dFormd-form-itemd-form-control 等核心组件
  • 各种输入组件需要单独导入对应的模块
  • 如果用的是 Angular 18 的 standalone 模式,记得把所有用到的模块都加到 imports 数组里

三、表单数据结构设计

我的表单包含基本信息、联系方式、工作信息和其他信息四个部分,总共 10 个字段。数据结构这样设计:

typescript 复制代码
userForm!: FormGroup;

// 性别选项
genderOptions = [
  { value: 'male', label: '男' },
  { value: 'female', label: '女' }
];

// 部门选项(注意:Select 组件需要 key-value 格式)
departmentOptions = [
  { key: 'tech', value: '技术部' },
  { key: 'product', value: '产品部' },
  { key: 'design', value: '设计部' },
  { key: 'operation', value: '运营部' },
  { key: 'hr', value: '人事部' }
];

// 城市选项
cityOptions = [
  { key: 'beijing', value: '北京' },
  { key: 'shanghai', value: '上海' },
  { key: 'guangzhou', value: '广州' },
  { key: 'shenzhen', value: '深圳' },
  { key: 'hangzhou', value: '杭州' }
];

这里有个坑:d-select 组件的 options 属性需要的是 { key, value } 格式,不是 { id, name } 或者 { value, label }。我一开始用错了格式,下拉框显示不出来,调试了半天才发现。

四、表单初始化和验证

表单初始化用 FormBuilder 来创建,验证规则直接在定义时加上:

typescript 复制代码
initForm(): void {
  this.userForm = this.fb.group({
    name: ['', [Validators.required, Validators.minLength(2), Validators.maxLength(20)]],
    age: [null, [Validators.required, Validators.min(18), Validators.max(100)]],
    gender: ['', Validators.required],
    birthday: [null, Validators.required],
    email: ['', [Validators.required, Validators.email]],
    phone: ['', [Validators.required, Validators.pattern(/^1[3-9]\d{9}$/)]],
    department: ['', Validators.required],
    city: ['', Validators.required],
    address: ['', [Validators.required, Validators.maxLength(200)]],
    description: ['', Validators.maxLength(500)]
  });
}

手机号验证用了正则表达式 /^1[3-9]\d{9}$/,只允许 1 开头、第二位是 3-9 的 11 位数字。年龄限制在 18-100 岁之间,这个根据业务需求调整。

五、模板结构

HTML 模板的结构比较清晰,用 dForm 指令包裹整个表单,每个字段用 d-form-item 包裹:

html 复制代码
<form dForm [formGroup]="userForm" (ngSubmit)="onSubmit()">
  <div class="form-section">
    <h3 class="section-title">基本信息</h3>
    
    <div class="form-row">
      <d-form-item class="form-item" [dHasFeedback]="true">
        <d-form-label>姓名 <span class="required-mark">*</span></d-form-label>
        <d-form-control>
          <input
            dTextInput
            formControlName="name"
            placeholder="请输入姓名"
          />
          <div class="error-message" *ngIf="isFieldInvalid('name')">
            {{ getFieldError("name") }}
          </div>
        </d-form-control>
      </d-form-item>
      <!-- 其他字段... -->
    </div>
  </div>
</form>

几个注意点:

  • dForm 指令必须加在 <form> 标签上
  • [dHasFeedback]="true" 用来显示验证反馈,注意是属性绑定,不能写成 dHasFeedback="true"
  • 错误提示用 *ngIf 控制显示,只在字段被触摸过且有错误时显示

六、常见问题处理

1. Textarea 组件的使用

d-textarea 其实是个指令,不是组件。应该这样用:

html 复制代码
<!-- 错误写法 -->
<d-textarea [rows]="4"></d-textarea>

<!-- 正确写法 -->
<textarea dTextarea [rows]="4"></textarea>

我一开始写成了组件形式,编译报错说找不到 rows 属性,后来查文档才发现是用法不对。

2. Select 组件的选项格式

d-selectoptions 属性需要特定格式:

typescript 复制代码
// 正确格式
departmentOptions = [
  { key: 'tech', value: '技术部' }
];

// 错误格式(这样不会显示)
departmentOptions = [
  { id: 'tech', name: '技术部' }
];

3. 表单验证错误提示

错误提示需要自己实现,DevUI 不会自动显示。我的做法是写两个辅助方法:

typescript 复制代码
// 检查字段是否有错误
isFieldInvalid(fieldName: string): boolean {
  const control = this.userForm.get(fieldName);
  return !!(control && control.invalid && control.touched);
}

// 获取字段错误信息
getFieldError(fieldName: string): string {
  const control = this.userForm.get(fieldName);
  if (control && control.touched && control.errors) {
    if (control.errors['required']) {
      return `${this.getFieldLabel(fieldName)}不能为空`;
    }
    if (control.errors['email']) {
      return '请输入有效的邮箱地址';
    }
    // ... 其他验证规则
  }
  return '';
}

这样可以在模板里统一处理错误提示,代码更清晰。

七、表单提交和重置

提交时先检查表单是否有效,无效的话把所有字段标记为 touched,这样错误提示就会显示出来:

typescript 复制代码
onSubmit(): void {
  if (this.userForm.valid) {
    console.log('表单数据:', this.userForm.value);
    // 这里可以调用 API 保存数据
    alert('保存成功!');
  } else {
    // 标记所有字段为 touched,显示验证错误
    Object.keys(this.userForm.controls).forEach(key => {
      this.userForm.get(key)?.markAsTouched();
    });
    alert('请检查表单输入是否正确!');
  }
}

重置功能比较简单,调用 reset() 方法就行。如果需要恢复默认值,可以在重置后重新 patchValue

八、样式优化

表单的样式可以根据项目需求自定义。我用了分组布局,每个分组有标题,字段用两列网格布局,移动端自动变成单列:

scss 复制代码
.form-section {
  margin-bottom: 32px;
  
  .section-title {
    font-size: 16px;
    font-weight: 600;
    margin: 0 0 20px 0;
    padding-bottom: 12px;
    border-bottom: 1px solid #e5e5e5;
  }
}

.form-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 24px;
  
  @media (max-width: 768px) {
    grid-template-columns: 1fr;
  }
}

总结

DevUI 的 Form 组件功能挺完善的,但用的时候要注意几个点:

  1. 组件导入要完整,特别是 standalone 模式下
  2. Select 组件的选项格式要用 { key, value }
  3. Textarea 是指令不是组件
  4. 验证错误提示需要自己实现
  5. dHasFeedback 要用属性绑定,不能用字符串

总的来说,DevUI Form 组件上手不难,但细节上容易踩坑。希望这篇文章能帮到正在使用 DevUI 的开发者。如果遇到其他问题,可以看看官方文档,或者去社区问问。

参考资源:

相关推荐
崔庆才丨静觅3 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60614 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了4 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅4 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
CDERgglUoMg4 小时前
BLDC直流无刷电机FOC控制 在Matlab/Simulink中实现了无刷直流电机的磁场定向...
华为云
崔庆才丨静觅5 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅5 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment5 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅5 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊5 小时前
jwt介绍
前端