Skip to content

Commit

Permalink
feat: add form onValidate, close #4817
Browse files Browse the repository at this point in the history
  • Loading branch information
tangjinzhou committed Oct 29, 2021
1 parent 42cc616 commit a5779f2
Show file tree
Hide file tree
Showing 9 changed files with 60 additions and 5 deletions.
6 changes: 5 additions & 1 deletion components/form/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export const formProps = {
onFieldsChange: { type: Function as PropType<Callbacks['onFieldsChange']> },
onFinish: { type: Function as PropType<Callbacks['onFinish']> },
onFinishFailed: { type: Function as PropType<Callbacks['onFinishFailed']> },
onValidate: { type: Function as PropType<Callbacks['onValidate']> },
};

export type FormProps = Partial<ExtractPropTypes<typeof formProps>>;
Expand All @@ -102,7 +103,7 @@ const Form = defineComponent({
}),
Item: FormItem,
useForm,
emits: ['finishFailed', 'submit', 'finish'],
emits: ['finishFailed', 'submit', 'finish', 'validate'],
setup(props, { emit, slots, expose, attrs }) {
const size = useInjectSize(props);
const { prefixCls, direction, form: contextForm } = useConfigInject('form', props);
Expand Down Expand Up @@ -355,6 +356,9 @@ const Form = defineComponent({
rules: computed(() => props.rules),
addField,
removeField,
onValidate: (name, status, errors) => {
emit('validate', name, status, errors);
},
});

watch(
Expand Down
17 changes: 16 additions & 1 deletion components/form/FormItem.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
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 PropTypes from '../_util/vue-types';
import Row from '../grid/Row';
Expand Down Expand Up @@ -213,6 +222,12 @@ export default defineComponent({
validateState.value = res.length ? 'error' : 'success';

errors.value = res.map(r => r.errors);

formContext.onValidate(
fieldName.value,
!errors.value.length,
errors.value.length ? toRaw(errors.value[0]) : null,
);
}
});

Expand Down
6 changes: 6 additions & 0 deletions components/form/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ export interface FormContextProps {
removeField: (eventKey: string) => void;
validateTrigger?: ComputedRef<string | string[]>;
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');
Expand All @@ -38,6 +43,7 @@ export const useInjectForm = () => {
model: computed(() => undefined),
rules: computed(() => undefined),
requiredMark: computed(() => false),
onValidate: () => {},
});
};

Expand Down
5 changes: 5 additions & 0 deletions components/form/demo/custom-validation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ See more advanced usage at [async-validator](https://github.com/yiminghe/async-v
:rules="rules"
v-bind="layout"
@finish="handleFinish"
@validate="handleValidate"
@finishFailed="handleFinishFailed"
>
<a-form-item has-feedback label="Password" name="pass">
Expand Down Expand Up @@ -112,6 +113,9 @@ export default defineComponent({
const resetForm = () => {
formRef.value.resetFields();
};
const handleValidate = (...args) => {
console.log(args);
};
return {
formState,
formRef,
Expand All @@ -120,6 +124,7 @@ export default defineComponent({
handleFinishFailed,
handleFinish,
resetForm,
handleValidate,
};
},
});
Expand Down
4 changes: 3 additions & 1 deletion components/form/demo/useForm-basic.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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 = () => {
validate()
.then(() => {
Expand Down
6 changes: 6 additions & 0 deletions components/form/index.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ A form consists of one or more form fields whose type includes input, textarea,
| Events Name | Description | Arguments | Version |
| --- | --- | --- | --- | --- |
| 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 |
| finishFailed | Trigger after submitting the form and verifying data failed | function({ values, errorFields, outOfDate }) | - | 2.0.0 |

Expand Down Expand Up @@ -234,5 +235,10 @@ function useForm(
) => Promise<RuleError[]>;
mergeValidateInfo: (items: ValidateInfo | ValidateInfo[]) => ValidateInfo;
clearValidate: (names?: namesType) => void;
onValidate?: (
name: string | number | string[] | number[],
status: boolean,
errorMsgs: string[] | null,
) => void;
};
```
6 changes: 6 additions & 0 deletions components/form/index.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/ORmcdeaoO/Form.svg
| 事件名称 | 说明 | 回调参数 | 版本 |
| --- | --- | --- | --- | --- |
| submit | 数据验证成功后回调事件 | Function(e:Event) ||
| validate | 任一表单项被校验后触发 | Function(name, status, errorMsgs) | |
| finish | 提交表单且数据验证成功后回调事件 | function(values) | - | 2.0.0 |
| finishFailed | 提交表单且数据验证失败后回调事件 | function({ values, errorFields, outOfDate }) | - | 2.0.0 |

Expand Down Expand Up @@ -232,5 +233,10 @@ function useForm(
) => Promise<RuleError[]>;
mergeValidateInfo: (items: ValidateInfo | ValidateInfo[]) => ValidateInfo;
clearValidate: (names?: namesType) => void;
onValidate?: (
name: string | number | string[] | number[],
status: boolean,
errorMsgs: string[] | null,
) => void;
};
```
5 changes: 5 additions & 0 deletions components/form/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,11 @@ export interface Callbacks<Values = any> {
onFieldsChange?: (changedFields: FieldData[], allFields: FieldData[]) => void;
onFinish?: (values: 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
Expand Down
10 changes: 8 additions & 2 deletions components/form/useForm.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
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 intersection from 'lodash-es/intersection';
import isEqual from 'lodash-es/isEqual';
Expand All @@ -8,7 +8,7 @@ import omit from 'lodash-es/omit';
import { validateRules } from './utils/validateUtil';
import { defaultValidateMessages } from './utils/messages';
import { allPromiseFinish } from './utils/asyncUtil';
import type { RuleError, ValidateMessages } from './interface';
import type { Callbacks, RuleError, ValidateMessages } from './interface';
import type { ValidateStatus } from './FormItem';

interface DebounceSettings {
Expand Down Expand Up @@ -98,6 +98,7 @@ function useForm(
deep?: boolean;
validateOnRuleChange?: boolean;
debounce?: DebounceSettings;
onValidate?: Callbacks['onValidate'];
},
): {
modelRef: Props | Ref<Props>;
Expand Down Expand Up @@ -252,6 +253,11 @@ function useForm(
const res = results.filter(result => result && result.errors.length);
validateInfos[name].validateStatus = res.length ? 'error' : 'success';
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;
Expand Down

0 comments on commit a5779f2

Please sign in to comment.