useForm
A composable that provides comprehensive form handling capabilities including state management, submission handling, validation integration, and server error processing.
Usage
ts
import { ref } from 'vue'
import { useForm } from '#imports'
const form = ref(null) // Optional Vuetify form reference
const { model, submit, submitting, serverErrors } = useForm({
initialValues: {
name: '',
email: '',
password: '',
},
form, // Optional Vuetify form reference
async onSubmit(values) {
await api.post('users', values)
},
})
Type Signature
ts
interface UseFormOptions<T> {
initialValues: T;
onSubmit: (values: T) => Promise<void>;
form?: Ref<VForm>; // Vuetify form component reference
}
function useForm<T>(options: UseFormOptions<T>): {
model: Ref<T>;
updateModel: (value: T | undefined) => void;
reset: () => void;
submit: () => Promise<void>;
submitting: Ref<boolean>;
save: () => Promise<void>;
loading: Ref<boolean>;
resetValidation: () => void;
serverErrors: ComputedRef<Record<keyof T, string>>;
checkServerErrors: (error: any) => void;
clearServerErrors: () => void;
untrackedServerErrors: Ref<string[]>;
}
Parameters
Parameter | Type | Description |
---|---|---|
options | UseFormOptions<T> | Configuration options for the form |
options.initialValues | T | Initial values for the form model |
options.onSubmit | (values: T) => Promise<void> | Function called when the form is submitted |
options.form | Ref<VForm> | Optional reference to a Vuetify form component for validation integration |
Return Value
Property | Type | Description |
---|---|---|
model | Ref<T> | Reactive form data model |
updateModel | (value: T | undefined) => void | Function to update the form model |
reset | () => void | Reset form values and validation state |
submit | () => Promise<void> | Handle form submission with validation |
submitting | Ref<boolean> | Whether form is currently being submitted |
save | () => Promise<void> | Alias for submit |
loading | Ref<boolean> | Alias for submitting |
resetValidation | () => void | Clear validation errors |
serverErrors | ComputedRef<Record<keyof T, string>> | Server-side validation errors |
checkServerErrors | (error: any) => void | Parse server error response |
clearServerErrors | () => void | Clear server validation errors |
untrackedServerErrors | Ref<string[]> | Generic server errors not tied to specific fields |
Features
- Automatic validation: Integrates with Vuetify's form validation system
- Server error handling: Processes API error responses into field-specific validation errors
- Form state management: Tracks form submission state
- Reset functionality: Easily reset form to initial values
- Type safety: Fully typed form values and errors
Examples
Basic Form with Vuetify
vue
<template>
<v-form ref="form">
<v-row dense>
<portal to="form-sites-title">
<span v-if="isNew">{{ $t('sites.form.actions.create.title') }}</span>
<span v-else>{{ $t('sites.form.actions.edit.title') }}</span>
</portal>
<v-col cols="12">
<v-text-field
v-model="model.name"
:error-messages="serverErrors.name"
:rules="rules.name"
:label="$t('sites.form.fields.name.label')"
/>
</v-col>
<v-col cols="9">
<v-text-field
v-model="model.address"
:error-messages="serverErrors.address"
:rules="rules.address"
:label="$t('sites.form.fields.address.label')"
/>
</v-col>
<v-col cols="3">
<v-text-field
v-model="model.address_number"
:error-messages="serverErrors.address_number"
:rules="rules.address_number"
:label="$t('sites.form.fields.address_number.label')"
/>
</v-col>
<v-col cols="9">
<v-text-field
v-model="model.city"
:error-messages="serverErrors.city"
:rules="rules.city"
:label="$t('sites.form.fields.city.label')"
/>
</v-col>
<v-col cols="3">
<v-text-field
v-model="model.province"
:error-messages="serverErrors.province"
:rules="rules.province"
:label="$t('sites.form.fields.province.label')"
/>
</v-col>
</v-row>
<portal to="form-sites-actions">
<WeBtnDeleteConfirm
v-if="!isNew"
class="mr-2"
:on-delete="deleteModel"
/>
<v-btn
color="primary"
size="large"
:loading="loading"
@click="save"
>
<v-icon start>
$save
</v-icon> {{ $t('forms.buttons.save') }}
</v-btn>
</portal>
</v-form>
</template>
<script setup lang="js">
const props = defineProps({
extraParams: {
type: Object,
default: () => ({}),
},
})
const emit = defineEmits(['deleted'])
const { t } = useI18n()
const defaultValues = {
name: null,
address: null,
address_number: null,
city: null,
province: null,
}
function load(id) {
const params = { with: '' }
return useApi().$get(`sites/${id}`, { params })
}
defineExpose({ load })
const modelValue = defineModel('modelValue', { type: Object, default: undefined })
const id = computed(() => modelValue.value?.id)
const isNew = computed(() => !id.value)
const { validatorRequired } = useValidators()
const {
save,
loading,
model,
updateModel,
resetValidation,
serverErrors,
} = useForm({
form: useTemplateRef('form'),
initialValues: defaultValues,
onSubmit,
})
const rules = computed(() => {
return {
name: [validatorRequired],
address: [],
address_number: [],
city: [],
province: [],
}
})
watch(modelValue, (val) => {
resetValidation()
updateModel(val)
}, { immediate: true })
async function onSubmit() {
const { ...values } = model.value
const data = {
...props.extraParams,
...values,
}
const method = isNew.value ? 'post' : 'put'
const url = isNew.value ? 'sites' : `sites/${id.value}`
const res = await useApi().$request({ method, url, data })
const message = isNew.value ? t('sites.form.messages.saveSuccess') : t('sites.form.messages.editSuccess')
push.success(message)
modelValue.value = res
}
async function deleteModel() {
await useApi().$delete(`sites/${id.value}`)
push.success(t('sites.form.messages.delete'))
emit('deleted')
}
</script>