diff --git a/content/input/form/index-en-US.md b/content/input/form/index-en-US.md index dc373fdf50..f9ffc536e5 100644 --- a/content/input/form/index-en-US.md +++ b/content/input/form/index-en-US.md @@ -2068,7 +2068,8 @@ render(WithFieldDemo2); | labelPosition | Location of label in Field, optional 'top', 'left', 'inset'
(inset label only partial component support) | string | 'top' | | labelWidth | Width of field'r label | string\|number | | | onChange | Callback invoked when form update, including Fields mount/unmount / value change /
blur / validation status change / error status change. | function (formState: object) | | -| onValueChange | Callback invoked when form values update | function (values: object, changedValue: object) | +| onErrorChange | Callback when the validation state of form updated. The first parameter: formState.errors, second parameter: name of the field that has changed and it's error message (available after v2.66) | function(values:object, changedError: object) | | +| onValueChange | Callback invoked when form values update. The first parameter: formState.values, second parameter: name of the field and it's value | function (values: object, changedValue: object) | | onReset | Callback invoked after clicked on reset button or executed `formApi.reset()` | function () | | | onSubmit | Callback invoked after clicked on submit button or executed `formApi.submit()`,
and all validation pass. | function (values: object, e: event) | | | onSubmitFail | Callback invoked after clicked on submit button or executed `formApi.submit()`,
but validate failed. | function (error: object, values: object, e: event) | | diff --git a/content/input/form/index.md b/content/input/form/index.md index 3d7b6b90b5..dae746b0fe 100644 --- a/content/input/form/index.md +++ b/content/input/form/index.md @@ -2066,6 +2066,7 @@ render(WithFieldDemo2); | labelWidth | 统一配置label 宽度 | string\|number | | | onChange | form 更新时触发,包括表单控件挂载/卸载/值变更/blur/验证状态变更/错误提示变更, 入参为 formState | function(formState:object) | | | onValueChange | form 的值被更新时触发,仅在表单控件值发生变化时触发。第一个入参为 formState.values,第二个入参为当前发生变化的 field | function(values:object, changedValue: object) | | +| onErrorChange | form 的校验状态被更新时触发。第一个入参为 formState.errors,第二个入参为当前发生变化的 field 的名称与校验结果(v2.66后提供) | function(values:object, changedError: object) | | | onReset | 点击 reset 按钮或调用 `formApi.reset()`时的回调函数 | function() | | | onSubmit | 点击 submit 按钮或调用 `formApi.submitForm()`,数据验证成功后的回调函数 | function(values:object, e: event) | | | onSubmitFail | 点击 submit 按钮或调用 `formApi.submitForm()`,数据验证失败后的回调函数 | function(errors:object, values:object, e: event) | | diff --git a/packages/semi-foundation/form/foundation.ts b/packages/semi-foundation/form/foundation.ts index ada8a1739c..d5809938c4 100644 --- a/packages/semi-foundation/form/foundation.ts +++ b/packages/semi-foundation/form/foundation.ts @@ -194,6 +194,7 @@ export default class FormFoundation extends BaseFoundation { } else { this.data.errors = result; this._adapter.notifyChange(this.data); + this.injectErrorToField(result); this._adapter.forceUpdate(); this._autoScroll(100); @@ -212,6 +213,7 @@ export default class FormFoundation extends BaseFoundation { this.data.errors = maybePromisedErrors; this.injectErrorToField(maybePromisedErrors); this._adapter.notifyChange(this.data); + this._adapter.forceUpdate(); this._autoScroll(100); reject(maybePromisedErrors); @@ -241,6 +243,7 @@ export default class FormFoundation extends BaseFoundation { Promise.all(promiseSet).then(() => { // After the centralized verification is completed, trigger notify and forceUpdate once. this._adapter.notifyChange(this.data); + this._adapter.forceUpdate(); const errors = this.getError(); if (this._isValid(targetFields)) { @@ -493,12 +496,14 @@ export default class FormFoundation extends BaseFoundation { const notNotify = opts && opts.notNotify; const notUpdate = opts && opts.notUpdate; ObjectUtil.set(this.data.errors, field, error); + + // The setError caused by centralized validation does not need to trigger notify, otherwise it will be called too frequently, as many times as there are fields - // 集中validate时,引起的setError不需要触发notify,否则会过于频繁调用,有多少个field就调用了多少次 if (!notNotify) { this._adapter.notifyChange(this.data); } - + this._adapter.notifyErrorChange(this.data.errors, { [field]: error }); + if (!notUpdate) { this._adapter.forceUpdate(callback); } diff --git a/packages/semi-foundation/form/interface.ts b/packages/semi-foundation/form/interface.ts index cd67180323..f0e16f16b9 100644 --- a/packages/semi-foundation/form/interface.ts +++ b/packages/semi-foundation/form/interface.ts @@ -17,6 +17,7 @@ export interface BaseFormAdapter

, S = Record void) => void; notifyChange: (formState: FormState) => void; notifyValueChange: (values: any, changedValues: any) => void; + notifyErrorChange: (errors: any, changedError: any) => void; notifyReset: () => void; getInitValues: () => Partial; getFormProps: (keys: undefined | string | Array) => any; diff --git a/packages/semi-ui/form/_story/Validate/validateDemo.jsx b/packages/semi-ui/form/_story/Validate/validateDemo.jsx index 38f9fcfbf8..1a1177a3ea 100644 --- a/packages/semi-ui/form/_story/Validate/validateDemo.jsx +++ b/packages/semi-ui/form/_story/Validate/validateDemo.jsx @@ -70,8 +70,10 @@ class ValidateFieldsDemo extends Component { autoScrollToError validateFields={this.syncValidate} onReset={v => console.log('reset')} - onChange={v => console.log(v)} - onValueChange={v => console.log('onValueChange')}> + // onChange={v => console.log(v)} + onValueChange={(values, changedField) => console.log('onValueChange', values, changedField)} + onErrorChange={(errors, changedField) => console.log('onErrorChange', errors, changedField)} + > @@ -142,7 +144,10 @@ class CustomValidateDemo extends Component { render() { return ( -

+ console.log(errors, changedField)} + > @@ -203,7 +208,11 @@ class PartValidAndResetDemo extends Component { render() { let options = ['a', 'b', 'c', 'd', 'b.name'].map(item => ({ label: item, value: item })); return ( - + console.log(errors, changedField)} + > @@ -258,7 +267,9 @@ class RulesValidateDemo extends Component { autoScrollToError onReset={v => console.log('reset')} onChange={v => console.log(v)} - onValueChange={v => console.log('onValueChange')}> + onValueChange={v => console.log('onValueChange')} + onErrorChange={(errors, changedField) => console.log(errors, changedField)} + > diff --git a/packages/semi-ui/form/baseForm.tsx b/packages/semi-ui/form/baseForm.tsx index e81bbcc8c0..133f2f2684 100644 --- a/packages/semi-ui/form/baseForm.tsx +++ b/packages/semi-ui/form/baseForm.tsx @@ -13,7 +13,7 @@ import { cloneDeep } from '../_utils/index'; import Slot from './slot'; import Section from './section'; import Label from './label'; -import ErrorMessage from './errorMessage'; +import ErrorMessage, { ReactFieldError } from './errorMessage'; import FormInputGroup from './group'; import { noop } from 'lodash'; import '@douyinfe/semi-foundation/form/form.scss'; @@ -93,6 +93,7 @@ class Form = any> extends BaseComponent = any> extends BaseComponent) => { this.props.onValueChange(values, changedValues); }, + notifyErrorChange: (errors: Record, changedError: Partial>) => { + this.props.onErrorChange(errors, changedError); + }, notifyReset: () => { this.props.onReset(); }, @@ -269,6 +273,7 @@ class Form = any> extends BaseComponent = any> { } - export interface BaseFormProps = any> extends Omit, 'children' | 'onChange' | 'onSubmit' | 'onReset'> { 'aria-label'?: React.AriaAttributes['aria-label']; onSubmit?: (values: Values, e?: React.FormEvent) => void; onSubmitFail?: (errors: Record, values: Partial, e?: React.FormEvent) => void; onReset?: () => void; onValueChange?: (values: Values, changedValue: Partial) => void; + onErrorChange?: (errors: Record, changedError?: Partial>) => void; onChange?: (formState: FormState) => void; allowEmpty?: boolean; validateFields?: (values: Values) => string | Partial>;