diff --git a/components/form/Form.tsx b/components/form/Form.tsx index d8715bfd6..95d229a2f 100755 --- a/components/form/Form.tsx +++ b/components/form/Form.tsx @@ -5,7 +5,7 @@ import classNames from '../_util/classNames'; import warning from '../_util/warning'; import type { FieldExpose } from './FormItem'; import FormItem from './FormItem'; -import { getNamePath, containsNamePath } from './utils/valueUtil'; +import { getNamePath, containsNamePath, cloneByNamePathList } from './utils/valueUtil'; import { defaultValidateMessages } from './utils/messages'; import { allPromiseFinish } from './utils/asyncUtil'; import { toArray } from './utils/typeUtil'; @@ -133,7 +133,6 @@ const Form = defineComponent({ ); const lastValidatePromise = ref(); const fields: Record = {}; - const addField = (eventKey: string, field: FieldExpose) => { fields[eventKey] = field; }; @@ -197,19 +196,15 @@ const Form = defineComponent({ } }; // eslint-disable-next-line no-unused-vars - const getFieldsValue = (nameList: NamePath[] | true = true) => { - const values: any = {}; - Object.values(fields).forEach(({ fieldName, fieldValue }) => { - values[fieldName.value] = fieldValue.value; - }); + const getFieldsValue = (nameList: InternalNamePath[] | true = true) => { if (nameList === true) { - return values; + const allNameList = []; + Object.values(fields).forEach(({ namePath }) => { + allNameList.push(namePath.value); + }); + return cloneByNamePathList(props.model, allNameList); } else { - const res: any = {}; - toArray(nameList as NamePath[]).forEach( - namePath => (res[namePath as string] = values[namePath as string]), - ); - return res; + return cloneByNamePathList(props.model, nameList); } }; const validateFields = (nameList?: NamePath[], options?: ValidateOptions) => { diff --git a/components/form/utils/valueUtil.ts b/components/form/utils/valueUtil.ts index 61cd19af2..382d5ddf4 100644 --- a/components/form/utils/valueUtil.ts +++ b/components/form/utils/valueUtil.ts @@ -1,5 +1,7 @@ import { toArray } from './typeUtil'; import type { InternalNamePath, NamePath } from '../interface'; +import get from '../../vc-util/get'; +import set from '../../vc-util/set'; /** * Convert name to internal supported format. @@ -12,6 +14,21 @@ export function getNamePath(path: NamePath | null): InternalNamePath { return toArray(path); } +export function getValue(store: T, namePath: InternalNamePath) { + const value = get(store, namePath); + return value; +} + +export function setValue( + store: T, + namePath: InternalNamePath, + value: any, + removeIfUndefined = false, +): T { + const newStore = set(store, namePath, value, removeIfUndefined); + return newStore; +} + export function containsNamePath(namePathList: InternalNamePath[], namePath: InternalNamePath) { return namePathList && namePathList.some(path => matchNamePath(path, namePath)); } @@ -50,6 +67,16 @@ export function setValues(store: T, ...restValues: T[]): T { ); } +export function cloneByNamePathList(store: T, namePathList: InternalNamePath[]): T { + let newStore = {} as T; + namePathList.forEach(namePath => { + const value = getValue(store, namePath); + newStore = setValue(newStore, namePath, value); + }); + + return newStore; +} + export function matchNamePath( namePath: InternalNamePath, changedNamePath: InternalNamePath | null, diff --git a/components/vc-util/get.ts b/components/vc-util/get.ts new file mode 100644 index 000000000..9ae45fc82 --- /dev/null +++ b/components/vc-util/get.ts @@ -0,0 +1,13 @@ +export default function get(entity: any, path: (string | number)[]) { + let current = entity; + + for (let i = 0; i < path.length; i += 1) { + if (current === null || current === undefined) { + return undefined; + } + + current = current[path[i]]; + } + + return current; +} diff --git a/components/vc-util/set.ts b/components/vc-util/set.ts new file mode 100644 index 000000000..d3d4a9442 --- /dev/null +++ b/components/vc-util/set.ts @@ -0,0 +1,51 @@ +import get from './get'; + +function internalSet( + entity: Entity, + paths: (string | number)[], + value: Value, + removeIfUndefined: boolean, +): Output { + if (!paths.length) { + return value as unknown as Output; + } + + const [path, ...restPath] = paths; + + let clone: Output; + if (!entity && typeof path === 'number') { + clone = [] as unknown as Output; + } else if (Array.isArray(entity)) { + clone = [...entity] as unknown as Output; + } else { + clone = { ...entity } as unknown as Output; + } + + // Delete prop if `removeIfUndefined` and value is undefined + if (removeIfUndefined && value === undefined && restPath.length === 1) { + delete clone[path][restPath[0]]; + } else { + clone[path] = internalSet(clone[path], restPath, value, removeIfUndefined); + } + + return clone; +} + +export default function set( + entity: Entity, + paths: (string | number)[], + value: Value, + removeIfUndefined = false, +): Output { + // Do nothing if `removeIfUndefined` and parent object not exist + if ( + paths.length && + removeIfUndefined && + value === undefined && + !get(entity, paths.slice(0, -1)) + ) { + return entity as unknown as Output; + } + + return internalSet(entity, paths, value, removeIfUndefined); +}