【OpenHarmony/HarmonyOs 】ArkUI 添加与编辑复用一个弹窗:表单状态管理实战

前言

添加网站和编辑网站需要的字段基本相同,如果分别写两个弹窗,会重复布局、校验和错误提示。LinkOS 链界使用 editingSiteId 区分模式,让同一套表单同时完成新增与更新。本文从状态设计一直讲到提交、防重复点击和异常处理。📝

一、表单需要哪些状态

@State showAddDialog: boolean = false;
@State editingSiteId: string = '';
@State siteTitleInput: string = '';
@State siteUrlInput: string = '';
@State submitting: boolean = false;
@State formError: string = '';

editingSiteId 表示新增,非空表示编辑。标题和按钮文字都由这个状态派生:

Text(this.editingSiteId ? '编辑自定义网站' : '添加自定义网站')
Button(this.editingSiteId ? '保存' : '添加')

不要再额外维护 isEditMode,否则两个状态可能互相矛盾。

二、打开新增模式

private openCreateDialog(): void {
  this.editingSiteId = '';
  this.siteTitleInput = '';
  this.siteUrlInput = '';
  this.formError = '';
  this.showAddDialog = true;
}

每次打开前重置旧值很重要。否则用户上次取消输入的内容可能出现在下一次新增中。

三、打开编辑模式并回填

private openEditDialog(item: UrlItem): void {
  this.editingSiteId = item.id;
  this.siteTitleInput = item.title;
  this.siteUrlInput = item.url;
  this.formError = '';
  this.showAddDialog = true;
}

页面只保存正在编辑的 ID 和表单草稿,不直接双向绑定原对象。用户点击取消时,原列表数据不会被提前修改。

四、输入控件与即时状态

TextInput({
  text: this.siteTitleInput,
  placeholder: '网站名称'
})
  .onChange((value: string) => {
    this.siteTitleInput = value;
    this.formError = '';
  })

TextInput({
  text: this.siteUrlInput,
  placeholder: 'https://example.com'
})
  .onChange((value: string) => {
    this.siteUrlInput = value;
    this.formError = '';
  })

输入阶段可以做基础提示,但最终校验必须在 Service 层再次执行,因为业务规则不能只依赖 UI。

五、统一提交函数

private async submitSiteForm(): Promise<void> {
  if (this.submitting) return;
  this.submitting = true;
  this.formError = '';

  const service = EntryManageService.getInstance();
  try {
    if (this.editingSiteId) {
      this.customSites = await service.updateCustomSite(
        this.editingSiteId,
        { title: this.siteTitleInput, url: this.siteUrlInput }
      );
    } else {
      this.customSites = await service.addCustomSite(
        this.siteTitleInput,
        this.siteUrlInput
      );
    }
    this.closeSiteDialog();
  } catch (error) {
    this.formError = this.mapSiteError(error);
  } finally {
    this.submitting = false;
  }
}

提交期间禁用按钮,可以防止连续点击创建重复数据。只有服务成功返回后才关闭弹窗;失败时保留用户输入,方便修改后重试。

六、把业务错误翻译成用户文案

Service 抛出稳定错误代码:

private mapSiteError(error: Object): string {
  const message = `${error}`;
  if (message.includes('EMPTY_TITLE')) return '请输入网站名称';
  if (message.includes('INVALID_URL')) return '请输入有效的 HTTPS 地址';
  if (message.includes('DUPLICATE_URL')) return '该网站已经收藏';
  if (message.includes('NOT_FOUND')) return '该网站已不存在,请刷新后重试';
  return '保存失败,请稍后重试';
}

错误最好显示在相关输入框附近。AlertDialog 更适合阻断性错误,普通表单校验使用行内文案更高效。

七、关闭时彻底清理

private closeSiteDialog(): void {
  this.showAddDialog = false;
  this.editingSiteId = '';
  this.siteTitleInput = '';
  this.siteUrlInput = '';
  this.formError = '';
}

遮罩关闭、取消按钮和成功提交都复用同一个关闭函数,避免某条路径忘记重置编辑 ID。

八、URL 规范化放在哪里

UI 可以提示用户输入 HTTPS,但最终规则应放在 Service:去空格、检查协议、去重、更新时间维护。这样未来从搜索建议“一键添加”时,即使不经过这个弹窗,也仍遵守同样规则。

若希望自动把 example.com 补成 https://example.com,应明确这是业务规则,并在提交前向用户展示规范化后的地址,避免悄悄改变输入含义。

九、并发与数据过期

用户打开编辑弹窗后,该记录可能被其他设备删除。Service 更新时应按 ID 再次查询,找不到返回 NOT_FOUND。云同步环境还可携带版本号,防止旧表单覆盖新修改。

十、测试清单 ✅

  • 新增模式字段为空且标题正确;
  • 编辑模式正确回填;
  • 取消后原数据不变;
  • 空标题、HTTP、重复 URL 均有准确提示;
  • 连续点击只产生一次提交;
  • 保存失败时输入仍保留;
  • 编辑成功保留原 createdAt
  • 关闭后再次打开没有旧状态。

十一、总结

复用弹窗不只是用一个三元表达式改标题。完整做法是让 editingSiteId 成为模式来源,表单使用独立草稿,提交统一进入 Service,成功后刷新数组,失败时保留现场,并在所有关闭路径清理状态。这套模式也能复用于分类、元服务入口和个人资料编辑。✨

img

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐