diff --git a/packages/core/src/baseeditableholder/BaseEditableHolder.vue b/packages/core/src/baseeditableholder/BaseEditableHolder.vue index f5ed9bde5..800e15824 100644 --- a/packages/core/src/baseeditableholder/BaseEditableHolder.vue +++ b/packages/core/src/baseeditableholder/BaseEditableHolder.vue @@ -76,7 +76,7 @@ export default { $formValue: { immediate: false, handler(newValue) { - if (this.$formName !== undefined && this.$pcForm?.states?.[this.$formName] && newValue !== this.d_value) { + if (this.$pcForm?.states?.[this.$formName] && newValue !== this.d_value) { this.d_value = newValue; } } @@ -112,7 +112,7 @@ export default { return this.d_value ?? this.$pcFormField?.initialValue ?? this.$pcForm?.initialValues?.[this.$formName]; }, $formValue() { - return this.$pcForm?.states?.[this.$formName]?.value; + return this.$pcFormField?.$field?.value ?? this.$pcForm?.states?.[this.$formName]?.value; }, controlled() { return this.$inProps.hasOwnProperty('modelValue') || (!this.$inProps.hasOwnProperty('modelValue') && !this.$inProps.hasOwnProperty('defaultValue')); diff --git a/packages/forms/src/form/Form.d.ts b/packages/forms/src/form/Form.d.ts index b95bc89b5..32ee01014 100644 --- a/packages/forms/src/form/Form.d.ts +++ b/packages/forms/src/form/Form.d.ts @@ -107,6 +107,16 @@ export interface FormSubmitEvent { reset: () => void; } +/** + * Reset events + */ +export interface FormResetEvent { + /** + * The original DOM event. + */ + originalEvent: Event; +} + /** * The state of a form field. */ @@ -247,6 +257,11 @@ export interface FormEmitsOptions { * @param {FormSubmitEvent} event - Custom submit event. */ submit: (event: FormSubmitEvent) => void; + /** + * Emitted when the form is reset. + * @param {FormResetEvent} event - Custom reset event. + */ + reset: (event: FormResetEvent) => void; } export declare type FormEmits = EmitFn; diff --git a/packages/forms/src/form/Form.vue b/packages/forms/src/form/Form.vue index 88af0f38f..e24d2222c 100644 --- a/packages/forms/src/form/Form.vue +++ b/packages/forms/src/form/Form.vue @@ -1,5 +1,5 @@ @@ -13,7 +13,7 @@ export default { name: 'Form', extends: BaseForm, inheritAttrs: false, - emits: ['submit'], + emits: ['submit', 'reset'], setup(props, { emit }) { const $form = useForm(props); @@ -27,10 +27,15 @@ export default { emit('submit', e); }); + const onReset = $form.handleReset((e) => { + emit('reset', e); + }); + return { register, onSubmit, - ...omit($form, ['handleSubmit']) + onReset, + ...omit($form, ['handleSubmit', 'handleReset']) }; } }; diff --git a/packages/forms/src/useform/index.d.ts b/packages/forms/src/useform/index.d.ts index e8c989102..6aaf9504f 100644 --- a/packages/forms/src/useform/index.d.ts +++ b/packages/forms/src/useform/index.d.ts @@ -11,8 +11,11 @@ export interface useFormFieldState { export interface useFormReturn { defineField: (field: string, options?: any) => any; + setFieldValue: (field: string, value: any) => void; handleSubmit: (event: any) => any; + handleReset: (event: any) => any; validate: (field: string) => any; + setValues: (values: Record) => void; reset: () => void; valid: boolean; states: Record; diff --git a/packages/forms/src/useform/index.js b/packages/forms/src/useform/index.js index d78018204..b4527b8db 100644 --- a/packages/forms/src/useform/index.js +++ b/packages/forms/src/useform/index.js @@ -7,6 +7,29 @@ function tryOnMounted(fn, sync = true) { else nextTick(fn); } +function watchPausable(source, callback, options) { + const isActive = ref(true); + + const stop = watch( + source, + (newValue, oldValue) => { + if (!isActive.value) return; + callback(newValue, oldValue); + }, + options + ); + + return { + stop, + pause: () => { + isActive.value = false; + }, + resume: () => { + isActive.value = true; + } + }; +} + export const useForm = (options = {}) => { const states = reactive({}); const fields = reactive({}); @@ -47,6 +70,8 @@ export const useForm = (options = {}) => { }; const defineField = (field, fieldOptions) => { + fields[field]?._watcher.stop(); + states[field] ||= getInitialState(field, fieldOptions?.initialValue); const props = mergeProps(resolve(fieldOptions, states[field])?.props, resolve(fieldOptions?.props, states[field]), { @@ -68,9 +93,7 @@ export const useForm = (options = {}) => { } }); - fields[field] = { props, states: states[field], options: fieldOptions }; - - watch( + const _watcher = watchPausable( () => states[field].value, (newValue, oldValue) => { if (states[field].pristine) { @@ -85,6 +108,8 @@ export const useForm = (options = {}) => { } ); + fields[field] = { props, states: states[field], options: fieldOptions, _watcher }; + return [states[field], props]; }; @@ -102,6 +127,16 @@ export const useForm = (options = {}) => { }; }; + const handleReset = (callback) => { + return async (event) => { + reset(); + + return callback({ + originalEvent: event + }); + }; + }; + const validate = async (field) => { const resolverOptions = Object.entries(states).reduce( (acc, [key, val]) => { @@ -147,7 +182,22 @@ export const useForm = (options = {}) => { }; const reset = () => { - Object.keys(states).forEach((field) => (fields[field].states = states[field] = getInitialState(field, fields[field]?.options?.initialValue))); + Object.keys(states).forEach(async (field) => { + const watcher = fields[field]._watcher; + + watcher.pause(); + fields[field].states = states[field] = getInitialState(field, fields[field]?.options?.initialValue); + await nextTick(); + watcher.resume(); + }); + }; + + const setFieldValue = (field, value) => { + states[field].value = value; + }; + + const setValues = (values) => { + Object.keys(values).forEach((field) => setFieldValue(field, values[field])); }; const validateOnMounted = () => { @@ -158,8 +208,11 @@ export const useForm = (options = {}) => { return { defineField, + setFieldValue, handleSubmit, + handleReset, validate, + setValues, reset, valid, states,