Skip to content

Commit

Permalink
feat: forItem.name支持多层级数据结构 (#278)
Browse files Browse the repository at this point in the history
Co-authored-by: kongjing@dian.so <apple@AppledeMacBook-Pro-2.local>
  • Loading branch information
zuolung and kongjing@dian.so authored Jun 24, 2022
1 parent 698d706 commit 8d18d8b
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 25 deletions.
59 changes: 59 additions & 0 deletions packages/vantui-doc/src/form/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,65 @@ function Demo() {
}
```

### 支持多层级数据结构

FormItem 的 name 属性支持数组的形式, 数组项为字符串的时候挂载到对象上,为数字的时候挂载到数组上
第一层固定为对象

```jsx
function Demo() {
const formIt = react.useRef(null)

const multFormItems = function () {
let jsx = []
for (let i = 0; i < 2; i++) {
jsx.push(
<>
<FormItem
label={`名称(${i + 1})`}
name={['useInfo', i, 'name']}
trigger="onInput"
valueFormat={(e) => e.detail.value}
>
<Input placeholder="请输入用户名" />
</FormItem>
<FormItem
label={`年龄(${i + 1})`}
name={['useInfo', i, 'age']}
trigger="onInput"
valueFormat={(e) => e.detail.value}
>
<Input placeholder="请输入年龄" />
</FormItem>
</>,
)
}
return jsx
}

return (
<Form ref={formIt}>
{multFormItems()}
<Button
className="van-button-submit"
formType="submit"
onClick={() =>
Dialog.alert({
message: `result: ${JSON.stringify(
formIt.current.getFieldsValue(),
)}`,
selector: 'form-demo3',
})
}
>
提交
</Button>
<Dialog id="form-demo3" />
</Form>
)
}
```

### 异步处理和自定义校验

- Uploader 的 onAfterRead 事件只返回变更的文件,展示的是多个文件的话需要重新设置
Expand Down
19 changes: 10 additions & 9 deletions packages/vantui/src/form-item/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export function FormItem(props: FormItemProps) {
const formInstance = useContext<IFormInstanceAPI>(FormContext)
const { registerValidateFields, dispatch, unRegisterValidate } = formInstance
const [, forceUpdate_] = useState({})
const _name = Array.isArray(name) ? name.join('.') : name

const onStoreChange = useMemo(() => {
const onStoreChange = {
Expand All @@ -50,29 +51,29 @@ export function FormItem(props: FormItemProps) {

useEffect(() => {
/* 注册表单 */
name &&
registerValidateFields(name, onStoreChange, { rules, required, label })
_name &&
registerValidateFields(_name, onStoreChange, { rules, required, label })

return function () {
name && unRegisterValidate(name)
_name && unRegisterValidate(_name)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [onStoreChange])

const getControlled = (child: any) => {
const props = { ...child.props }
if (!name) return props
if (!_name) return props
const trigger_ = props[trigger]

const handleChange = async (e: any) => {
let value = null

if (valueFormat) {
value = await valueFormat(e, name, formInstance)
value = await valueFormat(e, _name, formInstance)
} else {
value = e.detail
}
dispatch({ type: 'setFieldsValue' }, name, value)
dispatch({ type: 'setFieldsValue' }, _name, value)
if (trigger_) trigger_(e)
}
props[trigger] = handleChange
Expand All @@ -82,10 +83,10 @@ export function FormItem(props: FormItemProps) {
await handleChange(e)
}

dispatch({ type: 'validateFieldValue' }, name)
dispatch({ type: 'validateFieldValue' }, _name)
}
}
props[valueKey] = dispatch({ type: 'getFieldValue' }, name)
props[valueKey] = dispatch({ type: 'getFieldValue' }, _name)

return props
}
Expand All @@ -112,7 +113,7 @@ export function FormItem(props: FormItemProps) {
<Message
name={label}
feedback={feedback}
{...dispatch({ type: 'getFieldModel' }, name)}
{...dispatch({ type: 'getFieldModel' }, _name)}
/>
</View>
</View>
Expand Down
73 changes: 64 additions & 9 deletions packages/vantui/src/form/core/formstore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { unstable_batchedUpdates } from 'react-dom'
import { IFormInstanceAPI } from '../../../types/form'

type IAPI = keyof IFormInstanceAPI
type Iname = string | Array<string | number>

/* 对外接口 */
const formInstanceApi = [
Expand Down Expand Up @@ -82,23 +83,26 @@ class FormStore {
}

registerValidateFields(
name: string,
name_: Iname,
control: Record<string, any>,
model: Record<string, any>,
): void {
const name = Array.isArray(name_) ? name_.join('.') : name_
if (this.defaultFormValue[name])
model['value'] = this.defaultFormValue[name]
const validate = FormStore.createValidate(model)
this.model[name] = validate
this.control[name] = control
}

unRegisterValidate(name: string) {
unRegisterValidate(name_: Iname) {
const name = Array.isArray(name_) ? name_.join('.') : name_
delete this.model[name]
delete this.control[name]
}

notifyChange(name: string) {
notifyChange(name_: Iname) {
const name = Array.isArray(name_) ? name_.join('.') : name_
const controller = this.control[name]
if (controller) controller?.changeValue()
}
Expand All @@ -110,7 +114,8 @@ class FormStore {
})
}

setFieldsValue(name: string, modelValue: any): any {
setFieldsValue(name_: Iname, modelValue: any): any {
const name = Array.isArray(name_) ? name_.join('.') : name_
const model = this.model[name]
if (!model) return false
if (toString.call(modelValue) === '[Object, object]' && modelValue.value) {
Expand All @@ -125,18 +130,62 @@ class FormStore {
}
}

setValueClearStatus(model: Record<string, any>, name: string, value: any) {
setValueClearStatus(model: Record<string, any>, name_: Iname, value: any) {
const name = Array.isArray(name_) ? name_.join('.') : name_

model['value'] = value
model['status'] = 'pendding'
this.notifyChange(name)
}

static transformMultilevelData(data: Record<string, any>) {
const keys = Object.keys(data)
const hasMultiLevel = keys.some((item) => item.includes('.'))
if (hasMultiLevel) {
const res: Record<string, any> = {}
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
if (key) {
if (!key?.includes('.')) {
res[key] = data[key]
} else {
const ks = key.split('.')
let p = res
for (let j = 0; j < ks.length; j++) {
const k = ks[j] as string | number
if (p[k] && j !== ks.length - 1) {
p = p[k]
} else if (!p[k] && j !== ks.length - 1) {
const nextK = ks[j + 1] as string | number
if (
!isNaN(Number(nextK)) &&
typeof Number(nextK) === 'number'
) {
p[k] = []
} else if (typeof nextK === 'string') {
p[k] = {}
}
p = p[k]
} else {
p[k] = data[key]
}
}
}
}
}

return res
} else {
return data
}
}

getFieldsValue() {
const formData: any = {}
Object.keys(this.model).forEach((modelName) => {
formData[modelName] = this.model[modelName].value
})
return formData
return FormStore.transformMultilevelData(formData)
}

resetFields() {
Expand All @@ -145,19 +194,25 @@ class FormStore {
})
}

getFieldModel(name: string) {
getFieldModel(name_: Iname) {
const name = Array.isArray(name_) ? name_.join('.') : name_

const model = this.model[name]
return model ? model : {}
}

getFieldValue(name: string) {
getFieldValue(name_: Iname) {
const name = Array.isArray(name_) ? name_.join('.') : name_

const model = this.model[name]
if (!model && this.defaultFormValue[name])
return this.defaultFormValue[name]
return model ? model.value : null
}

validateFieldValue(name: string, forceUpdate = true) {
validateFieldValue(name_: Iname, forceUpdate = true) {
const name = Array.isArray(name_) ? name_.join('.') : name_

const model = this.model[name]
/* 记录上次状态 */
const lastStatus = model.status
Expand Down
21 changes: 14 additions & 7 deletions packages/vantui/types/form.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export interface FormItemProps extends StandardProps {
/**
* @description 对应表单字段名
*/
name: string
name: string | Array<string | number>
/**
* @description 第一级操作表单组件
*/
Expand Down Expand Up @@ -107,7 +107,7 @@ export interface FormItemProps extends StandardProps {
*/
valueFormat?: (
value: any,
name: string,
name: string | Array<string | number>,
IFormInstance: IFormInstanceAPI,
) => any
/**
Expand All @@ -127,7 +127,7 @@ export type IFormInstanceAPI = {
* @description 注册校验规则
*/
registerValidateFields: (
name: string,
name: string | Array<string | number>,
control: Record<string, any>,
model: Record<string, any>,
) => void
Expand All @@ -138,7 +138,7 @@ export type IFormInstanceAPI = {
/**
* @description 注册校验规则
*/
unRegisterValidate: (name: string) => void
unRegisterValidate: (name: string | Array<string | number>) => void
/**
* @description 重置表单
*/
Expand All @@ -150,15 +150,18 @@ export type IFormInstanceAPI = {
/**
* @description 设置单个表单值
*/
setFieldsValue: (name: string, modelValue: any) => any
setFieldsValue: (
name: string | Array<string | number>,
modelValue: any,
) => any
/**
* @description 获取所有表单值
*/
getFieldsValue: () => void
/**
* @description 获取单个表单值
*/
getFieldValue: (name: string) => any
getFieldValue: (name: string | Array<string | number>) => any
/**
* @description 校验表单,并获取错误信息和所有表单值
*/
Expand All @@ -175,7 +178,11 @@ export type IFormInstanceAPI = {
) => void,
) => void
} & {
dispatch: (params: { type: string }, name?: string, ...arg: any[]) => any
dispatch: (
params: { type: string },
name?: string | Array<string | number>,
...arg: any[]
) => any
setCallback: Record<
string,
undefined | ((values?: Record<string, any>) => void)
Expand Down

0 comments on commit 8d18d8b

Please sign in to comment.