Skip to content

Commit de14e46

Browse files
committed
feat(form): add validateTrigger, setFieldValue and disabled
1 parent 4e206b8 commit de14e46

File tree

13 files changed

+361
-247
lines changed

13 files changed

+361
-247
lines changed

src/packages/form/doc.en-US.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ import { Form } from '@nutui/nutui-react'
8080
| name | form name | `any` | `-` |
8181
| labelPosition | The position of the form item label | `top` \| `left` \| `right` | `right` |
8282
| starPosition | The red star position of the required form item label | `left` \| `right` | `left` |
83+
| validateTrigger | uniformly set the timing for fields to trigger validation | `string` \| `string[]` | `onChange` |
8384
| onFinish | Triggered after verification is successful | `(values: any) => void` | `-` |
8485
| onFinishFailed | Triggered when any form item fails validation | `(values: any, errorFields: any) => void` | `-` |
8586

@@ -125,7 +126,8 @@ Form.useForm() creates a Form instance, which is used to manage all data states.
125126
| --- | --- | --- |
126127
| getFieldValue | Get the value of the corresponding field name | `(name: NamePath) => any` |
127128
| getFieldsValue | Get values by a set of field names. Return according to the corresponding structure. Default return mounted field value, but you can use getFieldsValue(true) to get all values | `(name: NamePath \| boolean) => any` |
128-
| setFieldsValue | set field values | `(values) => void` |
129+
| setFieldsValue | Set the value of the form (the value will be passed directly to the form store. If you do not want the object passed in to be modified, please copy it and pass it in) | `(values) => void` |
130+
| setFieldValue | Set the value of the corresponding field name | `<T>(name: NamePath, value: T) => void` |
129131
| resetFields | Reset form prompt state | `() => void` |
130132
| submit | method to submit a form for validation | `Promise` |
131133

src/packages/form/doc.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ import { Form } from '@nutui/nutui-react'
7878
| name | 表单名称 | `any` | `-` |
7979
| labelPosition | 表单项 label 的位置 | `top` \| `left` \| `right` | `right` |
8080
| starPosition | 必填表单项 label 的红色星标位置 | `left` \| `right` | `left` |
81+
| validateTrigger | 统一设置字段触发验证的时机 | `string` \| `string[]` | `onChange` |
8182
| onFinish | 校验成功后触发 | `(values: any) => void` | `-` |
8283
| onFinishFailed | 任一表单项被校验失败后触发 | `(values: any, errorFields: any) => void` | `-` |
8384

@@ -124,7 +125,8 @@ Form.useForm()创建 Form 实例,用于管理所有数据状态。
124125
| --- | --- | --- |
125126
| getFieldValue | 获取对应字段名的值 | `(name: NamePath) => any` |
126127
| getFieldsValue | 获取一组字段名对应的值,会按照对应结构返回。默认返回现存字段值,当调用 getFieldsValue(true) 时返回所有值 | `(name: NamePath \| boolean) => any` |
127-
| setFieldsValue | 设置表单的值 | `(values) => void` |
128+
| setFieldsValue | 设置表单的值(该值将直接传入 form store 中。如果你不希望传入对象被修改,请克隆后传入) | `(values) => void` |
129+
| setFieldValue | 设置对应字段名的值 | `<T>(name: NamePath, value: T) => void` |
128130
| resetFields | 重置表单提示状态 | `() => void` |
129131
| submit | 提交表单进行校验的方法 | `Promise` |
130132

src/packages/form/doc.taro.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ import { Form } from '@nutui/nutui-react-taro'
7878
| name | 表单名称 | `any` | `-` |
7979
| labelPosition | 表单项 label 的位置 | \`\`'top' | 'left'\` | \`'right'\`\` |
8080
| starPosition | 必填表单项 label 的红色星标位置 | `left` \| `right` | `left` |
81+
| validateTrigger | 统一设置字段触发验证的时机 | `string` \| `string[]` | `onChange` |
8182
| onFinish | 校验成功后触发 | `(values: any) => void` | `-` |
8283
| onFinishFailed | 任一表单项被校验失败后触发 | `(values: any, errorFields: any) => void` | `-` |
8384

@@ -124,7 +125,8 @@ Form.useForm()创建 Form 实例,用于管理所有数据状态。
124125
| --- | --- | --- |
125126
| getFieldValue | 获取对应字段名的值 | `(name: NamePath) => any` |
126127
| getFieldsValue | 获取一组字段名对应的值,会按照对应结构返回。默认返回现存字段值,当调用 getFieldsValue(true) 时返回所有值 | `(name: NamePath \| boolean) => any` |
127-
| setFieldsValue | 设置表单的值 | `(values) => void` |
128+
| setFieldsValue | 设置表单的值(该值将直接传入 form store 中。如果你不希望传入对象被修改,请克隆后传入) | `(values) => void` |
129+
| setFieldValue | 设置对应字段名的值 | `<T>(name: NamePath, value: T) => void` |
128130
| resetFields | 重置表单提示状态 | `() => void` |
129131
| submit | 提交表单进行校验的方法 | `Promise` |
130132

src/packages/form/doc.zh-TW.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ import { Form } from '@nutui/nutui-react'
7979
| label | 标签名 | `ReactNode` | `-` |
8080
| labelPosition | 錶單項 label 的位置 | `top` \| `left` \| `right` | `right` |
8181
| starPosition | 必填錶單項 label 的紅色星標位置 | `left` \| `right` | `left` |
82+
| validateTrigger | 統一設定字段觸發驗證的時機 | `string` \| `string[]` | `onChange` |
8283
| onFinish | 校驗成功後觸發 | `(values: any) => void` | `-` |
8384
| onFinishFailed | 任一錶單項被校驗失敗後觸發 | `(values: any, errorFields: any) => void` | `-` |
8485

@@ -124,7 +125,8 @@ Form.useForm()創建 Form 實例,用於管理所有數據狀態。
124125
| --- | --- | --- |
125126
| getFieldValue | 獲取對應字段名的值 | `(name: NamePath) => any` |
126127
| getFieldsValue | 获取一组字段名对应的值,会按照对应结构返回。默认返回现存字段值,当调用 getFieldsValue(true) 时返回所有值 | `(name: NamePath \| boolean) => any` |
127-
| setFieldsValue | 設置錶單的值 | `(values) => void` |
128+
| setFieldsValue | 設定表單的值(該值將直接傳入 form store 中。如果你不希望傳入物件被修改,請複製後傳入) | `(values) => void` |
129+
| setFieldValue | 設定對應欄位名的值 | `<T>(name: NamePath, value: T) => void` |
128130
| resetFields | 重置錶單提示狀態 | `() => void` |
129131
| submit | 提交錶單進行校驗的方法 | `Promise` |
130132

src/packages/form/form.scss

Lines changed: 0 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,3 @@
11
@import '../cellgroup/cellgroup.scss';
22
@import '../cell/cell.scss';
33
@import '../formitem/formitem.scss';
4-
5-
.form-layout-right .nut-form-item-label {
6-
text-align: right;
7-
padding-right: 24px;
8-
white-space: nowrap;
9-
}
10-
11-
.form-layout-left .nut-form-item-label {
12-
position: relative;
13-
text-align: left;
14-
padding-left: 12px;
15-
white-space: nowrap;
16-
17-
.required {
18-
display: block;
19-
line-height: 1.5;
20-
position: absolute;
21-
left: 0.1em;
22-
}
23-
}
24-
25-
.form-layout-top .nut-form-item {
26-
flex-direction: column;
27-
align-items: flex-start;
28-
white-space: nowrap;
29-
}
30-
31-
.form-layout-top .nut-form-item-label {
32-
padding-bottom: 4px;
33-
display: block;
34-
padding-right: 24px;
35-
}
36-
37-
.form-layout-top .nut-form-item-body {
38-
margin-left: 0;
39-
width: 100%;
40-
}
41-
42-
[dir='rtl'] .form-layout-right .nut-form-item-label,
43-
.nut-rtl .form-layout-right .nut-form-item-label {
44-
text-align: left;
45-
padding-right: 0;
46-
padding-left: 24px;
47-
}
48-
49-
[dir='rtl'] .form-layout-left .nut-form-item-label,
50-
.nut-rtl .form-layout-left .nut-form-item-label {
51-
text-align: right;
52-
padding-left: 0;
53-
padding-right: 12px;
54-
55-
.required {
56-
left: auto;
57-
right: 0.1em;
58-
}
59-
}
60-
61-
[dir='rtl'] .form-layout-top .nut-form-item-label,
62-
.nut-rtl .form-layout-top .nut-form-item-label {
63-
padding-right: 0;
64-
padding-left: 24px;
65-
}
66-
67-
[dir='rtl'] .form-layout-top .nut-form-item-body,
68-
.nut-rtl .form-layout-top .nut-form-item-body {
69-
margin-left: 0;
70-
margin-right: 0;
71-
}

src/packages/form/form.taro.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React, { ReactNode } from 'react'
2+
import { Form as TForm } from '@tarojs/components'
23
import classNames from 'classnames'
34
import { Context } from './context'
45
import { SECRET, useForm } from './useform.taro'
@@ -11,7 +12,9 @@ export interface FormProps extends BasicComponent {
1112
initialValues: any
1213
name: string
1314
form: any
15+
disabled: boolean
1416
divider: boolean
17+
validateTrigger: string | string[] | false
1518
labelPosition: 'top' | 'left' | 'right'
1619
starPosition: 'left' | 'right'
1720
onFinish: (values: any) => void
@@ -22,7 +25,9 @@ const defaultProps = {
2225
...ComponentDefaults,
2326
labelPosition: 'right',
2427
starPosition: 'left',
28+
disabled: false,
2529
divider: false,
30+
validateTrigger: 'onChange',
2631
onFinish: (values) => {},
2732
onFinishFailed: (values, errorFields) => {},
2833
} as FormProps
@@ -43,8 +48,10 @@ export const Form = React.forwardRef<FormInstance, Partial<FormProps>>(
4348
children,
4449
initialValues,
4550
divider,
51+
disabled,
4652
onFinish,
4753
onFinishFailed,
54+
validateTrigger,
4855
labelPosition,
4956
starPosition,
5057
form,
@@ -77,7 +84,7 @@ export const Form = React.forwardRef<FormInstance, Partial<FormProps>>(
7784
}
7885

7986
return (
80-
<form
87+
<TForm
8188
className={classNames(
8289
classPrefix,
8390
PositionInfo[labelPosition],
@@ -96,12 +103,16 @@ export const Form = React.forwardRef<FormInstance, Partial<FormProps>>(
96103
}}
97104
>
98105
<Cell.Group divider={divider}>
99-
<Context.Provider value={formInstance}>{children}</Context.Provider>
106+
<Context.Provider
107+
value={{ formInstance, labelPosition, disabled, validateTrigger }}
108+
>
109+
{children}
110+
</Context.Provider>
100111
{footer ? (
101112
<Cell className={`${classPrefix}-footer`}>{footer}</Cell>
102113
) : null}
103114
</Cell.Group>
104-
</form>
115+
</TForm>
105116
)
106117
}
107118
)

src/packages/form/form.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ export interface FormProps extends BasicComponent {
1111
initialValues: any
1212
name: string
1313
form: any
14+
disabled: boolean
1415
divider: boolean
16+
validateTrigger: string | string[] | false
1517
labelPosition: 'top' | 'left' | 'right'
1618
starPosition: 'left' | 'right'
1719
onFinish: (values: any) => void
@@ -22,7 +24,9 @@ const defaultProps = {
2224
...ComponentDefaults,
2325
labelPosition: 'right',
2426
starPosition: 'left',
27+
disabled: false,
2528
divider: false,
29+
validateTrigger: 'onChange',
2630
onFinish: (values) => {},
2731
onFinishFailed: (values, errorFields) => {},
2832
} as FormProps
@@ -43,8 +47,10 @@ export const Form = React.forwardRef<FormInstance, Partial<FormProps>>(
4347
children,
4448
initialValues,
4549
divider,
50+
disabled,
4651
onFinish,
4752
onFinishFailed,
53+
validateTrigger,
4854
labelPosition,
4955
starPosition,
5056
form,
@@ -96,7 +102,11 @@ export const Form = React.forwardRef<FormInstance, Partial<FormProps>>(
96102
}}
97103
>
98104
<Cell.Group divider={divider}>
99-
<Context.Provider value={formInstance}>{children}</Context.Provider>
105+
<Context.Provider
106+
value={{ formInstance, labelPosition, disabled, validateTrigger }}
107+
>
108+
{children}
109+
</Context.Provider>
100110
{footer ? (
101111
<Cell className={`${classPrefix}-footer`}>{footer}</Cell>
102112
) : null}

src/packages/form/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export interface FormItemRuleWithoutValidator {
22
[key: string]: any
3+
34
regex?: RegExp
45
required?: boolean
56
message?: string
@@ -20,8 +21,9 @@ export interface Store {
2021

2122
export interface FormInstance<Values = any> {
2223
getFieldValue: (name: NamePath) => StoreValue
24+
setFieldValue: <T = any>(name: NamePath, value: T) => void
2325
getFieldsValue: (nameList: NamePath[] | true) => { [key: NamePath]: any }
24-
setFieldsValue: (value: any) => void
26+
setFieldsValue: (value: Store) => void
2527
resetFields: (fields?: NamePath[]) => void
2628
submit: () => void
2729
getInternal: (secret: string) => any

src/packages/form/useform.taro.ts

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { useRef } from 'react'
22
import Schema from 'async-validator'
3+
import { merge } from '@/utils/merge'
34
import {
4-
Store,
55
Callbacks,
6-
FormInstance,
76
FormFieldEntity,
7+
FormInstance,
88
NamePath,
9+
Store,
910
} from './types'
1011

1112
export const SECRET = 'NUT_FORM_INTERNAL'
@@ -74,28 +75,31 @@ class FormStore {
7475
return fieldsValue
7576
}
7677

78+
updateStore(nextStore: Store) {
79+
this.store = nextStore
80+
}
81+
7782
/**
7883
* 设置 form 的初始值,之后在 reset 的时候使用
7984
* @param values
8085
* @param init
8186
*/
8287

83-
setInitialValues = (values: Store, init: boolean) => {
88+
setInitialValues = (initialValues: Store, init: boolean) => {
89+
this.initialValues = initialValues || {}
8490
if (init) {
85-
this.initialValues = values
86-
this.store = values
91+
const nextStore = merge(initialValues, this.store)
92+
this.updateStore(nextStore)
8793
}
8894
}
8995

9096
/**
9197
* 存储组件数据
9298
* @param newStore { [name]: newValue }
9399
*/
94-
setFieldsValue = (newStore: any, needValidate = true) => {
95-
this.store = {
96-
...this.store,
97-
...newStore,
98-
}
100+
setFieldsValue = (newStore: any) => {
101+
const nextStore = merge(this.store, newStore)
102+
this.updateStore(nextStore)
99103
this.fieldEntities.forEach((entity: FormFieldEntity) => {
100104
const { name } = entity.props
101105
Object.keys(newStore).forEach((key) => {
@@ -113,7 +117,13 @@ class FormStore {
113117
item.entity.onStoreChange('update')
114118
}
115119
})
116-
needValidate && this.validateFields()
120+
}
121+
122+
setFieldValue = <T>(name: NamePath, value: T) => {
123+
const store = {
124+
[name]: value,
125+
}
126+
this.setFieldsValue(store)
117127
}
118128

119129
setCallback = (callback: Callbacks) => {
@@ -125,6 +135,12 @@ class FormStore {
125135

126136
validateEntities = async (entity: FormFieldEntity, errs: any[]) => {
127137
const { name, rules = [] } = entity.props
138+
139+
if (!name) {
140+
console.warn('Form field missing name property')
141+
return
142+
}
143+
128144
const descriptor: any = {}
129145
if (rules.length) {
130146
// 多条校验规则
@@ -142,7 +158,7 @@ class FormStore {
142158
// validator.messages()
143159
try {
144160
await validator.validate({ [name]: this.store?.[name] })
145-
} catch ({ errors }: any) {
161+
} catch ({ errors }) {
146162
if (errors) {
147163
errs.push(...(errors as any[]))
148164
this.errors[name] = errors
@@ -186,7 +202,8 @@ class FormStore {
186202

187203
resetFields = () => {
188204
this.errors.length = 0
189-
this.store = this.initialValues
205+
const nextStore = merge({}, this.initialValues)
206+
this.updateStore(nextStore)
190207
this.fieldEntities.forEach((entity: FormFieldEntity) => {
191208
entity.onStoreChange('reset')
192209
})
@@ -226,6 +243,7 @@ class FormStore {
226243
getFieldValue: this.getFieldValue,
227244
getFieldsValue: this.getFieldsValue,
228245
setFieldsValue: this.setFieldsValue,
246+
setFieldValue: this.setFieldValue,
229247
resetFields: this.resetFields,
230248
validateFields: this.validateFields,
231249
submit: this.submit,
@@ -245,5 +263,5 @@ export const useForm = (form?: FormInstance): [FormInstance] => {
245263
formRef.current = formStore.getForm() as FormInstance
246264
}
247265
}
248-
return [formRef.current]
266+
return [formRef.current as FormInstance]
249267
}

0 commit comments

Comments
 (0)