feat: add form onValidate, close #4817
							parent
							
								
									42cc616ed3
								
							
						
					
					
						commit
						a5779f2d18
					
				|  | @ -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>>; | ||||
|  | @ -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); | ||||
|  | @ -355,6 +356,9 @@ const Form = defineComponent({ | |||
|       rules: computed(() => props.rules), | ||||
|       addField, | ||||
|       removeField, | ||||
|       onValidate: (name, status, errors) => { | ||||
|         emit('validate', name, status, errors); | ||||
|       }, | ||||
|     }); | ||||
| 
 | ||||
|     watch( | ||||
|  |  | |||
|  | @ -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'; | ||||
|  | @ -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, | ||||
|             ); | ||||
|           } | ||||
|         }); | ||||
| 
 | ||||
|  |  | |||
|  | @ -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'); | ||||
|  | @ -38,6 +43,7 @@ export const useInjectForm = () => { | |||
|     model: computed(() => undefined), | ||||
|     rules: computed(() => undefined), | ||||
|     requiredMark: computed(() => false), | ||||
|     onValidate: () => {}, | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -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"> | ||||
|  | @ -112,6 +113,9 @@ export default defineComponent({ | |||
|     const resetForm = () => { | ||||
|       formRef.value.resetFields(); | ||||
|     }; | ||||
|     const handleValidate = (...args) => { | ||||
|       console.log(args); | ||||
|     }; | ||||
|     return { | ||||
|       formState, | ||||
|       formRef, | ||||
|  | @ -120,6 +124,7 @@ export default defineComponent({ | |||
|       handleFinishFailed, | ||||
|       handleFinish, | ||||
|       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 = () => { | ||||
|       validate() | ||||
|         .then(() => { | ||||
|  |  | |||
|  | @ -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 | | ||||
| 
 | ||||
|  | @ -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; | ||||
| }; | ||||
| ``` | ||||
|  |  | |||
|  | @ -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 | | ||||
| 
 | ||||
|  | @ -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; | ||||
| }; | ||||
| ``` | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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'; | ||||
|  | @ -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 { | ||||
|  | @ -98,6 +98,7 @@ function useForm( | |||
|     deep?: boolean; | ||||
|     validateOnRuleChange?: boolean; | ||||
|     debounce?: DebounceSettings; | ||||
|     onValidate?: Callbacks['onValidate']; | ||||
|   }, | ||||
| ): { | ||||
|   modelRef: Props | Ref<Props>; | ||||
|  | @ -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; | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 tangjinzhou
						tangjinzhou