feat: add form onValidate, close #4817
parent
42cc616ed3
commit
a5779f2d18
|
@ -84,6 +84,7 @@ export const formProps = {
|
||||||
onFieldsChange: { type: Function as PropType<Callbacks['onFieldsChange']> },
|
onFieldsChange: { type: Function as PropType<Callbacks['onFieldsChange']> },
|
||||||
onFinish: { type: Function as PropType<Callbacks['onFinish']> },
|
onFinish: { type: Function as PropType<Callbacks['onFinish']> },
|
||||||
onFinishFailed: { type: Function as PropType<Callbacks['onFinishFailed']> },
|
onFinishFailed: { type: Function as PropType<Callbacks['onFinishFailed']> },
|
||||||
|
onValidate: { type: Function as PropType<Callbacks['onValidate']> },
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FormProps = Partial<ExtractPropTypes<typeof formProps>>;
|
export type FormProps = Partial<ExtractPropTypes<typeof formProps>>;
|
||||||
|
@ -102,7 +103,7 @@ const Form = defineComponent({
|
||||||
}),
|
}),
|
||||||
Item: FormItem,
|
Item: FormItem,
|
||||||
useForm,
|
useForm,
|
||||||
emits: ['finishFailed', 'submit', 'finish'],
|
emits: ['finishFailed', 'submit', 'finish', 'validate'],
|
||||||
setup(props, { emit, slots, expose, attrs }) {
|
setup(props, { emit, slots, expose, attrs }) {
|
||||||
const size = useInjectSize(props);
|
const size = useInjectSize(props);
|
||||||
const { prefixCls, direction, form: contextForm } = useConfigInject('form', props);
|
const { prefixCls, direction, form: contextForm } = useConfigInject('form', props);
|
||||||
|
@ -355,6 +356,9 @@ const Form = defineComponent({
|
||||||
rules: computed(() => props.rules),
|
rules: computed(() => props.rules),
|
||||||
addField,
|
addField,
|
||||||
removeField,
|
removeField,
|
||||||
|
onValidate: (name, status, errors) => {
|
||||||
|
emit('validate', name, status, errors);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
|
|
@ -1,5 +1,14 @@
|
||||||
import type { PropType, ExtractPropTypes, ComputedRef } from 'vue';
|
import type { PropType, ExtractPropTypes, ComputedRef } from 'vue';
|
||||||
import { watch, defineComponent, computed, nextTick, ref, watchEffect, onBeforeUnmount } from 'vue';
|
import {
|
||||||
|
watch,
|
||||||
|
defineComponent,
|
||||||
|
computed,
|
||||||
|
nextTick,
|
||||||
|
ref,
|
||||||
|
watchEffect,
|
||||||
|
onBeforeUnmount,
|
||||||
|
toRaw,
|
||||||
|
} from 'vue';
|
||||||
import cloneDeep from 'lodash-es/cloneDeep';
|
import cloneDeep from 'lodash-es/cloneDeep';
|
||||||
import PropTypes from '../_util/vue-types';
|
import PropTypes from '../_util/vue-types';
|
||||||
import Row from '../grid/Row';
|
import Row from '../grid/Row';
|
||||||
|
@ -213,6 +222,12 @@ export default defineComponent({
|
||||||
validateState.value = res.length ? 'error' : 'success';
|
validateState.value = res.length ? 'error' : 'success';
|
||||||
|
|
||||||
errors.value = res.map(r => r.errors);
|
errors.value = res.map(r => r.errors);
|
||||||
|
|
||||||
|
formContext.onValidate(
|
||||||
|
fieldName.value,
|
||||||
|
!errors.value.length,
|
||||||
|
errors.value.length ? toRaw(errors.value[0]) : null,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,11 @@ export interface FormContextProps {
|
||||||
removeField: (eventKey: string) => void;
|
removeField: (eventKey: string) => void;
|
||||||
validateTrigger?: ComputedRef<string | string[]>;
|
validateTrigger?: ComputedRef<string | string[]>;
|
||||||
rules?: ComputedRef<{ [k: string]: ValidationRule[] | ValidationRule }>;
|
rules?: ComputedRef<{ [k: string]: ValidationRule[] | ValidationRule }>;
|
||||||
|
onValidate: (
|
||||||
|
name: string | number | string[] | number[],
|
||||||
|
status: boolean,
|
||||||
|
errors: string[] | null,
|
||||||
|
) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FormContextKey: InjectionKey<FormContextProps> = Symbol('formContextKey');
|
export const FormContextKey: InjectionKey<FormContextProps> = Symbol('formContextKey');
|
||||||
|
@ -38,6 +43,7 @@ export const useInjectForm = () => {
|
||||||
model: computed(() => undefined),
|
model: computed(() => undefined),
|
||||||
rules: computed(() => undefined),
|
rules: computed(() => undefined),
|
||||||
requiredMark: computed(() => false),
|
requiredMark: computed(() => false),
|
||||||
|
onValidate: () => {},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ See more advanced usage at [async-validator](https://github.com/yiminghe/async-v
|
||||||
:rules="rules"
|
:rules="rules"
|
||||||
v-bind="layout"
|
v-bind="layout"
|
||||||
@finish="handleFinish"
|
@finish="handleFinish"
|
||||||
|
@validate="handleValidate"
|
||||||
@finishFailed="handleFinishFailed"
|
@finishFailed="handleFinishFailed"
|
||||||
>
|
>
|
||||||
<a-form-item has-feedback label="Password" name="pass">
|
<a-form-item has-feedback label="Password" name="pass">
|
||||||
|
@ -112,6 +113,9 @@ export default defineComponent({
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
formRef.value.resetFields();
|
formRef.value.resetFields();
|
||||||
};
|
};
|
||||||
|
const handleValidate = (...args) => {
|
||||||
|
console.log(args);
|
||||||
|
};
|
||||||
return {
|
return {
|
||||||
formState,
|
formState,
|
||||||
formRef,
|
formRef,
|
||||||
|
@ -120,6 +124,7 @@ export default defineComponent({
|
||||||
handleFinishFailed,
|
handleFinishFailed,
|
||||||
handleFinish,
|
handleFinish,
|
||||||
resetForm,
|
resetForm,
|
||||||
|
handleValidate,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -72,7 +72,9 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
const { resetFields, validate, validateInfos } = useForm(modelRef, rulesRef);
|
const { resetFields, validate, validateInfos } = useForm(modelRef, rulesRef, {
|
||||||
|
onValidate: (...args) => console.log(...args),
|
||||||
|
});
|
||||||
const onSubmit = () => {
|
const onSubmit = () => {
|
||||||
validate()
|
validate()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
|
|
@ -49,6 +49,7 @@ A form consists of one or more form fields whose type includes input, textarea,
|
||||||
| Events Name | Description | Arguments | Version |
|
| Events Name | Description | Arguments | Version |
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| submit | Defines a function will be called if form data validation is successful. | Function(e:Event) | |
|
| submit | Defines a function will be called if form data validation is successful. | Function(e:Event) | |
|
||||||
|
| validate | triggers after a form item is validated | Function(name, status, errorMsgs) | | |
|
||||||
| finish | Trigger after submitting the form and verifying data successfully | function(values) | - | 2.0.0 |
|
| finish | Trigger after submitting the form and verifying data successfully | function(values) | - | 2.0.0 |
|
||||||
| finishFailed | Trigger after submitting the form and verifying data failed | function({ values, errorFields, outOfDate }) | - | 2.0.0 |
|
| finishFailed | Trigger after submitting the form and verifying data failed | function({ values, errorFields, outOfDate }) | - | 2.0.0 |
|
||||||
|
|
||||||
|
@ -234,5 +235,10 @@ function useForm(
|
||||||
) => Promise<RuleError[]>;
|
) => Promise<RuleError[]>;
|
||||||
mergeValidateInfo: (items: ValidateInfo | ValidateInfo[]) => ValidateInfo;
|
mergeValidateInfo: (items: ValidateInfo | ValidateInfo[]) => ValidateInfo;
|
||||||
clearValidate: (names?: namesType) => void;
|
clearValidate: (names?: namesType) => void;
|
||||||
|
onValidate?: (
|
||||||
|
name: string | number | string[] | number[],
|
||||||
|
status: boolean,
|
||||||
|
errorMsgs: string[] | null,
|
||||||
|
) => void;
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
|
@ -50,6 +50,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/ORmcdeaoO/Form.svg
|
||||||
| 事件名称 | 说明 | 回调参数 | 版本 |
|
| 事件名称 | 说明 | 回调参数 | 版本 |
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| submit | 数据验证成功后回调事件 | Function(e:Event) | | |
|
| submit | 数据验证成功后回调事件 | Function(e:Event) | | |
|
||||||
|
| validate | 任一表单项被校验后触发 | Function(name, status, errorMsgs) | |
|
||||||
| finish | 提交表单且数据验证成功后回调事件 | function(values) | - | 2.0.0 |
|
| finish | 提交表单且数据验证成功后回调事件 | function(values) | - | 2.0.0 |
|
||||||
| finishFailed | 提交表单且数据验证失败后回调事件 | function({ values, errorFields, outOfDate }) | - | 2.0.0 |
|
| finishFailed | 提交表单且数据验证失败后回调事件 | function({ values, errorFields, outOfDate }) | - | 2.0.0 |
|
||||||
|
|
||||||
|
@ -232,5 +233,10 @@ function useForm(
|
||||||
) => Promise<RuleError[]>;
|
) => Promise<RuleError[]>;
|
||||||
mergeValidateInfo: (items: ValidateInfo | ValidateInfo[]) => ValidateInfo;
|
mergeValidateInfo: (items: ValidateInfo | ValidateInfo[]) => ValidateInfo;
|
||||||
clearValidate: (names?: namesType) => void;
|
clearValidate: (names?: namesType) => void;
|
||||||
|
onValidate?: (
|
||||||
|
name: string | number | string[] | number[],
|
||||||
|
status: boolean,
|
||||||
|
errorMsgs: string[] | null,
|
||||||
|
) => void;
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
|
@ -155,6 +155,11 @@ export interface Callbacks<Values = any> {
|
||||||
onFieldsChange?: (changedFields: FieldData[], allFields: FieldData[]) => void;
|
onFieldsChange?: (changedFields: FieldData[], allFields: FieldData[]) => void;
|
||||||
onFinish?: (values: Values) => void;
|
onFinish?: (values: Values) => void;
|
||||||
onFinishFailed?: (errorInfo: ValidateErrorEntity<Values>) => void;
|
onFinishFailed?: (errorInfo: ValidateErrorEntity<Values>) => void;
|
||||||
|
onValidate?: (
|
||||||
|
name: string | number | string[] | number[],
|
||||||
|
status: boolean,
|
||||||
|
errors: string[] | null,
|
||||||
|
) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import type { Ref } from 'vue';
|
import type { Ref } from 'vue';
|
||||||
import { reactive, watch, nextTick, unref, shallowRef } from 'vue';
|
import { reactive, watch, nextTick, unref, shallowRef, toRaw } from 'vue';
|
||||||
import cloneDeep from 'lodash-es/cloneDeep';
|
import cloneDeep from 'lodash-es/cloneDeep';
|
||||||
import intersection from 'lodash-es/intersection';
|
import intersection from 'lodash-es/intersection';
|
||||||
import isEqual from 'lodash-es/isEqual';
|
import isEqual from 'lodash-es/isEqual';
|
||||||
|
@ -8,7 +8,7 @@ import omit from 'lodash-es/omit';
|
||||||
import { validateRules } from './utils/validateUtil';
|
import { validateRules } from './utils/validateUtil';
|
||||||
import { defaultValidateMessages } from './utils/messages';
|
import { defaultValidateMessages } from './utils/messages';
|
||||||
import { allPromiseFinish } from './utils/asyncUtil';
|
import { allPromiseFinish } from './utils/asyncUtil';
|
||||||
import type { RuleError, ValidateMessages } from './interface';
|
import type { Callbacks, RuleError, ValidateMessages } from './interface';
|
||||||
import type { ValidateStatus } from './FormItem';
|
import type { ValidateStatus } from './FormItem';
|
||||||
|
|
||||||
interface DebounceSettings {
|
interface DebounceSettings {
|
||||||
|
@ -98,6 +98,7 @@ function useForm(
|
||||||
deep?: boolean;
|
deep?: boolean;
|
||||||
validateOnRuleChange?: boolean;
|
validateOnRuleChange?: boolean;
|
||||||
debounce?: DebounceSettings;
|
debounce?: DebounceSettings;
|
||||||
|
onValidate?: Callbacks['onValidate'];
|
||||||
},
|
},
|
||||||
): {
|
): {
|
||||||
modelRef: Props | Ref<Props>;
|
modelRef: Props | Ref<Props>;
|
||||||
|
@ -252,6 +253,11 @@ function useForm(
|
||||||
const res = results.filter(result => result && result.errors.length);
|
const res = results.filter(result => result && result.errors.length);
|
||||||
validateInfos[name].validateStatus = res.length ? 'error' : 'success';
|
validateInfos[name].validateStatus = res.length ? 'error' : 'success';
|
||||||
validateInfos[name].help = res.length ? res.map(r => r.errors) : '';
|
validateInfos[name].help = res.length ? res.map(r => r.errors) : '';
|
||||||
|
options?.onValidate?.(
|
||||||
|
name,
|
||||||
|
!res.length,
|
||||||
|
res.length ? toRaw(validateInfos[name].help[0]) : null,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return promise;
|
return promise;
|
||||||
|
|
Loading…
Reference in New Issue