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: form add onErrorChange #2484

Merged
merged 5 commits into from
Sep 12, 2024
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
3 changes: 2 additions & 1 deletion content/input/form/index-en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -2068,7 +2068,8 @@ render(WithFieldDemo2);
| labelPosition | Location of label in Field, optional 'top', 'left', 'inset' <br/> (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 / <br/> 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()`, <br/>and all validation pass. | function (values: object, e: event) | |
| onSubmitFail | Callback invoked after clicked on submit button or executed `formApi.submit()`,<br/> but validate failed. | function (error: object, values: object, e: event) | |
Expand Down
1 change: 1 addition & 0 deletions content/input/form/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) | |
Expand Down
9 changes: 7 additions & 2 deletions packages/semi-foundation/form/foundation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ export default class FormFoundation extends BaseFoundation<BaseFormAdapter> {
} else {
this.data.errors = result;
this._adapter.notifyChange(this.data);

this.injectErrorToField(result);
this._adapter.forceUpdate();
this._autoScroll(100);
Expand All @@ -212,6 +213,7 @@ export default class FormFoundation extends BaseFoundation<BaseFormAdapter> {
this.data.errors = maybePromisedErrors;
this.injectErrorToField(maybePromisedErrors);
this._adapter.notifyChange(this.data);

this._adapter.forceUpdate();
this._autoScroll(100);
reject(maybePromisedErrors);
Expand Down Expand Up @@ -241,6 +243,7 @@ export default class FormFoundation extends BaseFoundation<BaseFormAdapter> {
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)) {
Expand Down Expand Up @@ -493,12 +496,14 @@ export default class FormFoundation extends BaseFoundation<BaseFormAdapter> {
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);
}
Expand Down
1 change: 1 addition & 0 deletions packages/semi-foundation/form/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface BaseFormAdapter<P = Record<string, any>, S = Record<string, any
forceUpdate: (callback?: () => void) => void;
notifyChange: (formState: FormState) => void;
notifyValueChange: (values: any, changedValues: any) => void;
notifyErrorChange: (errors: any, changedError: any) => void;
notifyReset: () => void;
getInitValues: () => Partial<Values>;
getFormProps: (keys: undefined | string | Array<string>) => any;
Expand Down
21 changes: 16 additions & 5 deletions packages/semi-ui/form/_story/Validate/validateDemo.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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)}
>
<Form.InputGroup label="group" style={{ width: 600 }}>
<Input field="group.name" style={{ width: 280 }} />
<Input field="group.sort" style={{ width: 290 }} />
Expand Down Expand Up @@ -142,7 +144,10 @@ class CustomValidateDemo extends Component {

render() {
return (
<Form autoScrollToError>
<Form
autoScrollToError
onErrorChange={(errors, changedField) => console.log(errors, changedField)}
>
<Input field="name" validate={this.asyncValidate} trigger="blur" />
<Input field="familyName" validate={this.validateName} trigger="blur" name="familyName" />
<Input field="code" validate={this.asyncValidate} trigger={['change', 'mount']} />
Expand Down Expand Up @@ -203,7 +208,11 @@ class PartValidAndResetDemo extends Component {
render() {
let options = ['a', 'b', 'c', 'd', 'b.name'].map(item => ({ label: item, value: item }));
return (
<Form getFormApi={this.getFormApi} autoScrollToError>
<Form
getFormApi={this.getFormApi}
autoScrollToError
onErrorChange={(errors, changedField) => console.log(errors, changedField)}
>
<Input field="a[1]" validate={this.validate} trigger="blur" />
<Input field="a[0]" validate={this.validate} trigger="blur" />
<Input field="ackk" validate={this.validate} trigger="blur" />
Expand Down Expand Up @@ -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)}
>
<Input field="panel[0].a" trigger="custom" rules={[{ required: true, message: '字段不能为空' }]} />
<Input field="panel[0].b" trigger="custom" rules={[{ required: true, message: '字段不能为空' }]} />
<Input field="panel[0].c" trigger="custom" rules={[{ required: true, message: '字段不能为空' }]} />
Expand Down
7 changes: 6 additions & 1 deletion packages/semi-ui/form/baseForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -93,6 +93,7 @@ class Form<Values extends Record<string, any> = any> extends BaseComponent<BaseF
onSubmit: noop,
onReset: noop,
onValueChange: noop,
onErrorChange: noop,
layout: 'vertical',
labelPosition: 'top',
allowEmpty: false,
Expand Down Expand Up @@ -180,6 +181,9 @@ class Form<Values extends Record<string, any> = any> extends BaseComponent<BaseF
notifyValueChange: (values: Values, changedValues: Partial<Values>) => {
this.props.onValueChange(values, changedValues);
},
notifyErrorChange: (errors: Record<keyof Values, ReactFieldError>, changedError: Partial<Record<keyof Values, ReactFieldError>>) => {
this.props.onErrorChange(errors, changedError);
},
notifyReset: () => {
this.props.onReset();
},
Expand Down Expand Up @@ -269,6 +273,7 @@ class Form<Values extends Record<string, any> = any> extends BaseComponent<BaseF
onChange,
onSubmit,
onSubmitFail,
onErrorChange,
onValueChange,
component,
render,
Expand Down
4 changes: 2 additions & 2 deletions packages/semi-ui/form/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export type CommonexcludeType = {

export type RadioCheckboxExcludeProps = {
defaultValue?: any;
chekced?: boolean;
checked?: boolean;
defaultChecked?: boolean;
field: string
};
Expand Down Expand Up @@ -97,13 +97,13 @@ export interface FormFCChild<K extends Record<string, any> = any> {
}



export interface BaseFormProps <Values extends Record<string, any> = any> extends Omit<React.FormHTMLAttributes<HTMLFormElement>, 'children' | 'onChange' | 'onSubmit' | 'onReset'> {
'aria-label'?: React.AriaAttributes['aria-label'];
onSubmit?: (values: Values, e?: React.FormEvent<HTMLFormElement>) => void;
onSubmitFail?: (errors: Record<keyof Values, FieldError>, values: Partial<Values>, e?: React.FormEvent<HTMLFormElement>) => void;
onReset?: () => void;
onValueChange?: (values: Values, changedValue: Partial<Values>) => void;
onErrorChange?: (errors: Record<keyof Values, FieldError>, changedError?: Partial<Record<keyof Values, FieldError>>) => void;
onChange?: (formState: FormState<Values>) => void;
allowEmpty?: boolean;
validateFields?: (values: Values) => string | Partial<AllErrors<Values>>;
Expand Down
Loading