关于合并表单数据域和原始数据的设计

背景

项目的前端主要使用了如下的技术

  • React库
  • 开发语言使用了TypeScript
  • UI组件库使用了Ant Design和Ant Design Pro

问题

笔者尝试了多次用精炼的标题来概括文章的内容,但是本文章的标题应该并不能清晰地表述出文章的内容。所以,笔者这里再累述一下要解决的问题,叙述有点枯燥和冗长。读者可以先看余下内容,然后来阅读或许更容易理解问题原因。

问题原因:在一个用户列表页面中,选中一个用户信息打开编辑页面进行修改,需要把用户的数据权限信息(分组是外键关联信息)删除,然后提交给后台保存。在编辑用户信息的页面,开发员使用了Ant Design中Form作为编辑页面的表单,该表单编辑了用户信息的部分,比如由用户自己维护的信息并不在这个该表单中编辑。所以需要合并表单编辑过的数据集和以前原有的数据集,当两个数据集有相同的属性时则采用表单提交的数据。在合并两个数据集的过程中,使用简单的let res = {...oldObj, ...newObj}时,表单页面中删除的数据权限仍然会出现在res中,因为oldObj中有数据权限。所以提交给后台的数据还是包含了数据权限,导致删除数据权限失败。

解决办法

假设有个类型为A,有两个部分A类的实例aNew和aOld,把aNew和aOld合成类A的对象a,有如下的要求

  • aOld和aNew中都有的属性时,属性值取aNew中的属性值
  • 当某属性只存在一个实例aNew或者aOld中时,取相应实例的属性值
  • 当指定的属性在aNew中不存在时,也不能取aOld中的属性
  • 上述约定中的属性在类A中只能是可选的,必选的属性不能空
  • 编译时必须能校验上述约定的属性名的正确性,以防止拼写错误和利于重构

代码

定义用户类,用户类中的数据权限sysVision是可选的, IdNameVo是一种类型先当作any类型看待。

export type UserVo = {
  id: number | undefined;
  name: string;
  nickName: string;
  password: string;
  gender: string;
  usable: number;
  remark: string;
  sysRole: IdNameVo;
  kind: string;
  sysVision?: IdNameVo;
  sysOrganization: IdNameVo;
  job: string;
  email: string;
  phone: string;
  sysRegion: IdNameVo;
  address: string;
} 

定义合并两个实例的函数

export type IsOptional<T, K extends keyof T> = { [K1 in Exclude<keyof T, K>]: T[K1] } & { K?: T[K] } extends T ? K : never

export type OptionalKeys<T> = { [K in keyof T]: IsOptional<T, K> }[keyof T]

export function  mergeOmitIfNotExist<T extends object>(newObj: any, oldObj: any, ...attrs: OptionalKeys<T>[]): T {
  // 用新对象值覆盖旧对象
  let r = {...oldObj, ...newObj};  
  for (let i = 0; i < attrs.length; ++i) {
    let item = attrs[i];
    if (!newObj.hasOwnProperty(item)) {
      // 移除新对象中没有的属性
      delete r[item];
    }
  }
  return r;
}

在Ant Design Form的表单提交函数中使用,props.value是旧值,newObj是该表单中编辑字段域内容,当用户在表单中移除了数据权限时,合并后待提交保存的数据中也会移除该数据权限字段。

    <ModalForm
      width={MODALFRM_WIDTH}
      title={props.title}
      open={props.visible}
      onFinish={(newObj:any)=>{
        let r = mergeOmitIfNotExist<UserVo>(newObj, props.value, 'sysVision')
        props.onSubmit(r);
        return Promise.resolve();}}
      modalProps={{
        destroyOnClose: true,
        onCancel: ()=>props.onCancel()
      }}
      initialValues = {props.value}
    >

小结

经过上面的设计、封装及利用了TypeScript编译时的元数据来改进重构性和避免拼写错误。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值