Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: forItem.name支持多层级数据结构 #278

Merged
merged 1 commit into from
Jun 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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