diff --git a/demos/vue-3/src/views/Home.vue b/demos/vue-3/src/views/Home.vue
index 55d1e2a..8971ca6 100644
--- a/demos/vue-3/src/views/Home.vue
+++ b/demos/vue-3/src/views/Home.vue
@@ -1,12 +1,12 @@
diff --git a/demos/vue-3/src/views/ResetForm.vue b/demos/vue-3/src/views/ResetForm.vue
new file mode 100644
index 0000000..6e85472
--- /dev/null
+++ b/demos/vue-3/src/views/ResetForm.vue
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/.vitepress/config.js b/docs/.vitepress/config.js
index 286c859..3c97c0a 100644
--- a/docs/.vitepress/config.js
+++ b/docs/.vitepress/config.js
@@ -88,6 +88,10 @@ const config = {
text: 'Slots',
link: '/guide/advanced/slots',
},
+ {
+ text: 'Reset Form',
+ link: '/guide/advanced/reset-form',
+ },
],
},
],
diff --git a/docs/guide/advanced/reset-form.md b/docs/guide/advanced/reset-form.md
new file mode 100644
index 0000000..bb7f470
--- /dev/null
+++ b/docs/guide/advanced/reset-form.md
@@ -0,0 +1,76 @@
+By default, form controls are resetted after `submit` event.
+
+It's possible to deactivate this funcionality by setting `resetAfterSubmit` to `false` on the `Form Options`.
+
+You can find and example [here](https://vue-dynamic-forms-demos.alvarosaburido.dev/reset-after-submit).
+
+```typescript
+export default {
+ data() {
+ return {
+ form: {
+ id: 'my-awesome-form',
+ fields: {
+ name: TextField({
+ label: 'Name',
+ }),
+ email: EmailField({
+ label: 'Email',
+ }),
+ },
+ },
+ options: {
+ resetAfterSubmit: false,
+ },
+ }
+ },
+}
+```
+
+# Manual Reset
+
+It's possible to reset the form controls and validation manually by accesing the `form` reference and calling the methods directly.
+
+```html
+
+```
+
+You can see the example [here](https://vue-dynamic-forms-demos.alvarosaburido.dev/reset-form).
+
+## Options API
+
+You can access the dom element ref above by using template refs `this.$ref`
+
+```typescript
+
+methods: {
+ resetForm() {
+ this.$refs.formRef.resetForm()
+ }
+}
+
+```
+
+## Composition API
+
+You can access the dom element ref above by using template refs `const formRef = ref(null)`.
+
+::: warning
+Remember to add the ref to the return statement on the `setup` function so it's available on the template.
+:::
+
+```typescript
+
+setup() {
+ const formRef = ref(null);
+
+ function resetForm() {
+ formRef.value.resetForm()
+ }
+
+ return {
+ formRef
+ }
+}
+
+```
diff --git a/src/components/dynamic-form/DynamicForm.vue b/src/components/dynamic-form/DynamicForm.vue
index 8258280..e7d708d 100644
--- a/src/components/dynamic-form/DynamicForm.vue
+++ b/src/components/dynamic-form/DynamicForm.vue
@@ -36,16 +36,16 @@
diff --git a/src/composables/useDynamicForm.ts b/src/composables/useDynamicForm.ts
index 2a774b9..eb02e04 100644
--- a/src/composables/useDynamicForm.ts
+++ b/src/composables/useDynamicForm.ts
@@ -1,4 +1,4 @@
-import { diff } from 'deep-object-diff';
+import { diff } from 'deep-object-diff'
import {
FieldTypes,
FormControl,
@@ -9,7 +9,7 @@ import {
DynamicForm,
FormFields,
FormOptions,
-} from '/@/core/models';
+} from '/@/core/models'
import {
computed,
ComputedRef,
@@ -18,104 +18,104 @@ import {
ref,
Ref,
toRaw,
-} from 'vue';
-import { deepClone, hasValue, removeEmpty } from '/@/core/utils/helpers';
-import { FieldControl } from '/@/core/factories';
-import { useDebounceFn } from './useDebounce';
-import { DynamicFormsOptions } from '/@/dynamicForms';
+} from 'vue'
+import { deepClone, hasValue, removeEmpty } from '/@/core/utils/helpers'
+import { FieldControl } from '/@/core/factories'
+import { useDebounceFn } from './useDebounce'
+import { DynamicFormsOptions } from '/@/dynamicForms'
interface DynamicFormComposition {
- controls: Ref
[]>;
- forceValidation: Ref;
- formValues: ComputedRef<(string | { [key: string]: boolean })[]>;
- formOptions: Ref;
- errors: ComputedRef>;
- isValid: ComputedRef;
- normalizedControls: ComputedRef>;
- deNormalizedScopedSlots: ComputedRef;
+ controls: Ref[]>
+ forceValidation: Ref
+ formValues: ComputedRef<(string | { [key: string]: boolean })[]>
+ formOptions: Ref
+ errors: ComputedRef>
+ isValid: ComputedRef
+ normalizedControls: ComputedRef>
+ deNormalizedScopedSlots: ComputedRef
// eslint-disable-next-line @typescript-eslint/no-explicit-any
- formattedOptions: ComputedRef;
- valueChange: (event: InputEvent) => void;
- onBlur: (event: InputEvent) => void;
- onValidate: (event: ValidationEvent) => void;
- handleSubmit: () => void;
- validateAll: () => void;
- mapControls: (empty?: boolean) => void;
- findControlByName: (name: string | unknown) => FormControl;
- resetForm: () => void;
- detectChanges: (fields: FormFields) => void;
- onOptionsChanged: (options: FormOptions) => void;
+ formattedOptions: ComputedRef
+ valueChange: (event: InputEvent) => void
+ onBlur: (event: InputEvent) => void
+ onValidate: (event: ValidationEvent) => void
+ handleSubmit: () => void
+ validateAll: () => void
+ mapControls: (empty?: boolean) => void
+ findControlByName: (name: string | unknown) => FormControl
+ resetForm: () => void
+ detectChanges: (fields: FormFields) => void
+ onOptionsChanged: (options: FormOptions) => void
}
export function useDynamicForm(
form: DynamicForm,
ctx: {
// eslint-disable-next-line @typescript-eslint/ban-types
- slots: {};
- emit: (arg0: string, arg1: { [x: string]: string | number }) => void;
+ slots: {}
+ emit: (arg0: string, arg1: { [x: string]: string | number }) => void
},
options?: DynamicFormsOptions,
): DynamicFormComposition {
- let cache = deepClone(toRaw(form.fields));
+ let cache = deepClone(toRaw(form.fields))
- const controls: Ref[]> = ref([]);
+ const controls: Ref[]> = ref([])
const formOptions: Ref = ref({
resetAfterSubmit: true,
...options?.form,
...form?.options,
- });
- const forceValidation = ref(false);
+ })
+ const forceValidation = ref(false)
- const deNormalizedScopedSlots = computed(() => Object.keys(ctx.slots));
+ const deNormalizedScopedSlots = computed(() => Object.keys(ctx.slots))
const normalizedControls = computed(() => {
- const normalizedControls = {};
+ const normalizedControls = {}
controls.value.forEach(element => {
- normalizedControls[element.name] = element;
- });
- return normalizedControls;
- });
+ normalizedControls[element.name] = element
+ })
+ return normalizedControls
+ })
const isValid = computed(() => {
- const hasInvalidControls = controls.value.some(control => !control.valid);
- return !hasInvalidControls;
- });
+ const hasInvalidControls = controls.value.some(control => !control.valid)
+ return !hasInvalidControls
+ })
const formValues = computed(() => {
return removeEmpty(
controls.value.reduce((prev, curr) => {
- const obj = {};
+ const obj = {}
obj[curr.name] =
curr.type === FieldTypes.NUMBER
? parseFloat(`${curr.value}`)
- : curr.value;
+ : curr.value
return {
...prev,
...obj,
- };
+ }
}, {}),
- );
- });
+ )
+ })
const errors = computed(() => {
return controls.value
? controls.value.reduce((prev, curr) => {
- const errors = Object.keys(curr.errors || {}) || [];
+ const errors = Object.keys(curr.errors || {}) || []
if (errors.length > 0) {
- const error = {};
- error[curr.name] = curr.errors;
+ const error = {}
+ error[curr.name] = curr.errors
return {
...prev,
...error,
- };
+ }
}
- return prev;
+ return prev
}, {})
- : {};
- });
+ : {}
+ })
const formattedOptions = computed(() => {
- const opts = formOptions.value;
+ const opts = formOptions.value
if (opts) {
const {
@@ -125,7 +125,7 @@ export function useDynamicForm(
netlify,
netlifyHoneypot,
autocomplete,
- } = opts;
+ } = opts
return {
class: customClass,
style: customStyles,
@@ -133,11 +133,11 @@ export function useDynamicForm(
'data-netlify': netlify,
'data-netlify-honeypot': netlifyHoneypot,
autocomplete: autocomplete ? 'on' : 'off',
- };
+ }
} else {
- return;
+ return
}
- });
+ })
function mapControls(empty = false) {
const controlArray =
@@ -152,61 +152,64 @@ export function useDynamicForm(
...field,
name: key,
}),
- ) || [];
+ ) || []
if (form.fieldOrder) {
controls.value = controlArray.sort(
(a: FormControl, b: FormControl) =>
form.fieldOrder.indexOf(a.name) - form.fieldOrder.indexOf(b.name),
- );
+ )
} else {
- controls.value = controlArray;
+ controls.value = controlArray
+ }
+ if (empty) {
+ debounceEmitChanges(formValues.value)
}
}
function findControlByName(name: string | unknown) {
- const updatedCtrl = controls.value.find(control => control.name === name);
- return updatedCtrl;
+ const updatedCtrl = controls.value.find(control => control.name === name)
+ return updatedCtrl
}
function emitChanges(changes: FormChanges) {
- ctx.emit('change', changes);
+ ctx.emit('change', changes)
}
- const debounceEmitChanges = useDebounceFn(emitChanges, 300);
+ const debounceEmitChanges = useDebounceFn(emitChanges, 300)
function valueChange(event: InputEvent) {
if (hasValue(event.value)) {
- const updatedCtrl = findControlByName(event.name);
+ const updatedCtrl = findControlByName(event.name)
if (updatedCtrl) {
- updatedCtrl.value = event.value as string;
- updatedCtrl.dirty = true;
+ updatedCtrl.value = event.value as string
+ updatedCtrl.dirty = true
}
- debounceEmitChanges(formValues.value);
+ debounceEmitChanges(formValues.value)
}
}
function onBlur({ name }: InputEvent) {
- const updatedCtrl = findControlByName(name);
+ const updatedCtrl = findControlByName(name)
if (updatedCtrl) {
- updatedCtrl.touched = true;
+ updatedCtrl.touched = true
}
}
function onValidate({ name, errors, valid }: ValidationEvent) {
- const updatedCtrl = findControlByName(name);
+ const updatedCtrl = findControlByName(name)
if (updatedCtrl) {
updatedCtrl.errors = removeEmpty({
...updatedCtrl.errors,
...errors,
- });
- updatedCtrl.valid = valid;
+ })
+ updatedCtrl.valid = valid
}
}
function detectChanges(fields) {
- const changes = diff(cache, deepClone(fields));
+ const changes = diff(cache, deepClone(fields))
Object.entries(changes).forEach(([key, value]) => {
- const ctrl = findControlByName(key);
+ const ctrl = findControlByName(key)
if (ctrl) {
Object.entries(value).forEach(([change, newValue]) => {
if (change === 'options' || change === 'validations') {
@@ -214,48 +217,48 @@ export function useDynamicForm(
ctrl[change][optKey] = {
...ctrl[change][optKey],
...optValue,
- };
- });
+ }
+ })
} else {
- ctrl[change] = newValue;
+ ctrl[change] = newValue
}
- });
+ })
}
- });
- cache = deepClone(toRaw(fields));
+ })
+ cache = deepClone(toRaw(fields))
}
function onOptionsChanged(changes) {
- Object.assign(formOptions.value, changes);
+ Object.assign(formOptions.value, changes)
}
function resetForm() {
- mapControls(true);
- forceValidation.value = false;
+ mapControls(true)
+ forceValidation.value = false
}
async function handleSubmit() {
- validateAll();
+ validateAll()
- await nextTick();
+ await nextTick()
if (isValid.value) {
- ctx.emit('submitted', formValues.value);
+ ctx.emit('submitted', formValues.value)
if (formOptions.value.resetAfterSubmit) {
- resetForm();
+ resetForm()
}
} else {
- ctx.emit('error', errors.value);
+ ctx.emit('error', errors.value)
}
}
function validateAll() {
- forceValidation.value = true;
+ forceValidation.value = true
}
onMounted(() => {
- mapControls();
- });
+ mapControls()
+ })
return {
controls,
@@ -277,5 +280,5 @@ export function useDynamicForm(
resetForm,
detectChanges,
onOptionsChanged,
- };
+ }
}