Skip to content

Commit a5779f2

Browse files
committed
feat: add form onValidate, close #4817
1 parent 42cc616 commit a5779f2

9 files changed

+60
-5
lines changed

components/form/Form.tsx

+5-1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ export const formProps = {
8484
onFieldsChange: { type: Function as PropType<Callbacks['onFieldsChange']> },
8585
onFinish: { type: Function as PropType<Callbacks['onFinish']> },
8686
onFinishFailed: { type: Function as PropType<Callbacks['onFinishFailed']> },
87+
onValidate: { type: Function as PropType<Callbacks['onValidate']> },
8788
};
8889

8990
export type FormProps = Partial<ExtractPropTypes<typeof formProps>>;
@@ -102,7 +103,7 @@ const Form = defineComponent({
102103
}),
103104
Item: FormItem,
104105
useForm,
105-
emits: ['finishFailed', 'submit', 'finish'],
106+
emits: ['finishFailed', 'submit', 'finish', 'validate'],
106107
setup(props, { emit, slots, expose, attrs }) {
107108
const size = useInjectSize(props);
108109
const { prefixCls, direction, form: contextForm } = useConfigInject('form', props);
@@ -355,6 +356,9 @@ const Form = defineComponent({
355356
rules: computed(() => props.rules),
356357
addField,
357358
removeField,
359+
onValidate: (name, status, errors) => {
360+
emit('validate', name, status, errors);
361+
},
358362
});
359363

360364
watch(

components/form/FormItem.tsx

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
import type { PropType, ExtractPropTypes, ComputedRef } from 'vue';
2-
import { watch, defineComponent, computed, nextTick, ref, watchEffect, onBeforeUnmount } from 'vue';
2+
import {
3+
watch,
4+
defineComponent,
5+
computed,
6+
nextTick,
7+
ref,
8+
watchEffect,
9+
onBeforeUnmount,
10+
toRaw,
11+
} from 'vue';
312
import cloneDeep from 'lodash-es/cloneDeep';
413
import PropTypes from '../_util/vue-types';
514
import Row from '../grid/Row';
@@ -213,6 +222,12 @@ export default defineComponent({
213222
validateState.value = res.length ? 'error' : 'success';
214223

215224
errors.value = res.map(r => r.errors);
225+
226+
formContext.onValidate(
227+
fieldName.value,
228+
!errors.value.length,
229+
errors.value.length ? toRaw(errors.value[0]) : null,
230+
);
216231
}
217232
});
218233

components/form/context.ts

+6
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ export interface FormContextProps {
1919
removeField: (eventKey: string) => void;
2020
validateTrigger?: ComputedRef<string | string[]>;
2121
rules?: ComputedRef<{ [k: string]: ValidationRule[] | ValidationRule }>;
22+
onValidate: (
23+
name: string | number | string[] | number[],
24+
status: boolean,
25+
errors: string[] | null,
26+
) => void;
2227
}
2328

2429
export const FormContextKey: InjectionKey<FormContextProps> = Symbol('formContextKey');
@@ -38,6 +43,7 @@ export const useInjectForm = () => {
3843
model: computed(() => undefined),
3944
rules: computed(() => undefined),
4045
requiredMark: computed(() => false),
46+
onValidate: () => {},
4147
});
4248
};
4349

components/form/demo/custom-validation.vue

+5
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ See more advanced usage at [async-validator](https://github.com/yiminghe/async-v
2626
:rules="rules"
2727
v-bind="layout"
2828
@finish="handleFinish"
29+
@validate="handleValidate"
2930
@finishFailed="handleFinishFailed"
3031
>
3132
<a-form-item has-feedback label="Password" name="pass">
@@ -112,6 +113,9 @@ export default defineComponent({
112113
const resetForm = () => {
113114
formRef.value.resetFields();
114115
};
116+
const handleValidate = (...args) => {
117+
console.log(args);
118+
};
115119
return {
116120
formState,
117121
formRef,
@@ -120,6 +124,7 @@ export default defineComponent({
120124
handleFinishFailed,
121125
handleFinish,
122126
resetForm,
127+
handleValidate,
123128
};
124129
},
125130
});

components/form/demo/useForm-basic.vue

+3-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,9 @@ export default defineComponent({
7272
},
7373
],
7474
});
75-
const { resetFields, validate, validateInfos } = useForm(modelRef, rulesRef);
75+
const { resetFields, validate, validateInfos } = useForm(modelRef, rulesRef, {
76+
onValidate: (...args) => console.log(...args),
77+
});
7678
const onSubmit = () => {
7779
validate()
7880
.then(() => {

components/form/index.en-US.md

+6
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ A form consists of one or more form fields whose type includes input, textarea,
4949
| Events Name | Description | Arguments | Version |
5050
| --- | --- | --- | --- | --- |
5151
| submit | Defines a function will be called if form data validation is successful. | Function(e:Event) | |
52+
| validate | triggers after a form item is validated | Function(name, status, errorMsgs) | | |
5253
| finish | Trigger after submitting the form and verifying data successfully | function(values) | - | 2.0.0 |
5354
| finishFailed | Trigger after submitting the form and verifying data failed | function({ values, errorFields, outOfDate }) | - | 2.0.0 |
5455

@@ -234,5 +235,10 @@ function useForm(
234235
) => Promise<RuleError[]>;
235236
mergeValidateInfo: (items: ValidateInfo | ValidateInfo[]) => ValidateInfo;
236237
clearValidate: (names?: namesType) => void;
238+
onValidate?: (
239+
name: string | number | string[] | number[],
240+
status: boolean,
241+
errorMsgs: string[] | null,
242+
) => void;
237243
};
238244
```

components/form/index.zh-CN.md

+6
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/ORmcdeaoO/Form.svg
5050
| 事件名称 | 说明 | 回调参数 | 版本 |
5151
| --- | --- | --- | --- | --- |
5252
| submit | 数据验证成功后回调事件 | Function(e:Event) ||
53+
| validate | 任一表单项被校验后触发 | Function(name, status, errorMsgs) | |
5354
| finish | 提交表单且数据验证成功后回调事件 | function(values) | - | 2.0.0 |
5455
| finishFailed | 提交表单且数据验证失败后回调事件 | function({ values, errorFields, outOfDate }) | - | 2.0.0 |
5556

@@ -232,5 +233,10 @@ function useForm(
232233
) => Promise<RuleError[]>;
233234
mergeValidateInfo: (items: ValidateInfo | ValidateInfo[]) => ValidateInfo;
234235
clearValidate: (names?: namesType) => void;
236+
onValidate?: (
237+
name: string | number | string[] | number[],
238+
status: boolean,
239+
errorMsgs: string[] | null,
240+
) => void;
235241
};
236242
```

components/form/interface.ts

+5
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,11 @@ export interface Callbacks<Values = any> {
155155
onFieldsChange?: (changedFields: FieldData[], allFields: FieldData[]) => void;
156156
onFinish?: (values: Values) => void;
157157
onFinishFailed?: (errorInfo: ValidateErrorEntity<Values>) => void;
158+
onValidate?: (
159+
name: string | number | string[] | number[],
160+
status: boolean,
161+
errors: string[] | null,
162+
) => void;
158163
}
159164

160165
// eslint-disable-next-line @typescript-eslint/no-explicit-any

components/form/useForm.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { Ref } from 'vue';
2-
import { reactive, watch, nextTick, unref, shallowRef } from 'vue';
2+
import { reactive, watch, nextTick, unref, shallowRef, toRaw } from 'vue';
33
import cloneDeep from 'lodash-es/cloneDeep';
44
import intersection from 'lodash-es/intersection';
55
import isEqual from 'lodash-es/isEqual';
@@ -8,7 +8,7 @@ import omit from 'lodash-es/omit';
88
import { validateRules } from './utils/validateUtil';
99
import { defaultValidateMessages } from './utils/messages';
1010
import { allPromiseFinish } from './utils/asyncUtil';
11-
import type { RuleError, ValidateMessages } from './interface';
11+
import type { Callbacks, RuleError, ValidateMessages } from './interface';
1212
import type { ValidateStatus } from './FormItem';
1313

1414
interface DebounceSettings {
@@ -98,6 +98,7 @@ function useForm(
9898
deep?: boolean;
9999
validateOnRuleChange?: boolean;
100100
debounce?: DebounceSettings;
101+
onValidate?: Callbacks['onValidate'];
101102
},
102103
): {
103104
modelRef: Props | Ref<Props>;
@@ -252,6 +253,11 @@ function useForm(
252253
const res = results.filter(result => result && result.errors.length);
253254
validateInfos[name].validateStatus = res.length ? 'error' : 'success';
254255
validateInfos[name].help = res.length ? res.map(r => r.errors) : '';
256+
options?.onValidate?.(
257+
name,
258+
!res.length,
259+
res.length ? toRaw(validateInfos[name].help[0]) : null,
260+
);
255261
}
256262
});
257263
return promise;

0 commit comments

Comments
 (0)