背景
项目的前端主要使用了如下的技术
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编译时的元数据来改进重构性和避免拼写错误。