useForm (Vue)
useForm() es el punto de entrada en Vue. Crea un FormController reactivo y expone refs computados con todo el state.
function useForm( options: MaybeRefOrGetter<FormControllerOptions>): UseFormReturn;
interface FormControllerOptions { schema: { modelSchema: Record<string, FieldDefinition>; locale?: 'es' | 'en' | 'pt-BR'; }; initialValues?: Record<string, any>; validateOn?: 'change' | 'blur' | 'submit'; onSubmit?: (values: Record<string, any>) => Promise<void> | void;}El argumento puede ser un objeto literal, un ref o un getter. Si pasas un getter, el form se reconstruye cuando cambien las dependencias reactivas.
Lo que retorna
Sección titulada «Lo que retorna»interface UseFormReturn { // Refs computados reactivos values: ComputedRef<Record<string, any>>; errors: ComputedRef<Record<string, string[]>>; touched: ComputedRef<Record<string, boolean>>; isValid: ComputedRef<boolean>; isDirty: ComputedRef<boolean>; isSubmitting: ComputedRef<boolean>; fields: ComputedRef<FieldDescriptor[]>;
// Acciones (sin .value, son métodos) setValue(name: string, value: any): void; setValues(partial: Record<string, any>): void; touch(name: string): void; touchAll(): void; validate(): boolean; submit(): Promise<void>; reset(values?: Record<string, any>): void;
// Bajo nivel controller: Ref<FormController>;}Uso básico
Sección titulada «Uso básico»<script setup>import { useForm, CoFormRenderer } from '@prolibu-suite/cobalt-form-vue';
const form = useForm(() => ({ schema: { modelSchema: { email: { type: 'String', required: true, format: 'email' }, password: { type: 'String', required: true, minLength: 8 }, }, locale: 'es', }, onSubmit: async (values) => { await api.login(values); },}));</script>
<template> <CoFormRenderer :form="form" /></template>Con eso ya tienes:
- Render automático de campos según el schema
- Validación en vivo con AJV
- Mensajes traducidos a español
- Submit habilitado solo si el form es válido
Acceder al state reactivo
Sección titulada «Acceder al state reactivo»<script setup>const form = useForm(() => ({ /* ... */ }));
// Leerconsole.log(form.values.value.email); // unwrap con .valueconsole.log(form.errors.value.email);console.log(form.isValid.value);
// Watchear cambiosimport { watch } from 'vue';watch(form.values, (next) => { console.log('values changed:', next);}, { deep: true });
watch(form.isDirty, (dirty) => { if (dirty) console.log('hay cambios sin guardar');});</script>
<template> <p v-if="!form.isValid.value">El formulario tiene errores</p> <p v-if="form.isDirty.value">Hay cambios sin guardar</p> <pre>{{ form.values.value }}</pre></template>Acciones programáticas
Sección titulada «Acciones programáticas»// Setear un valorform.setValue('email', 'foo@bar.com');
// Setear varios (merge, no replace)form.setValues({ email: 'foo@bar.com', age: 30 });
// Marcar como touched (muestra errores)form.touch('email');form.touchAll();
// Forzar validaciónconst isOk = form.validate();
// Submit (corre validate + onSubmit)await form.submit();
// Reset a initialValues + defaults del schemaform.reset();
// Reset a valores específicosform.reset({ email: 'nuevo@ejemplo.com' });Schema reactivo
Sección titulada «Schema reactivo»Si el schema depende de props o state (ej. cambia según el rol del usuario), pasa un getter:
<script setup>const props = defineProps({ role: String });
const form = useForm(() => ({ schema: { modelSchema: props.role === 'admin' ? adminSchema : userSchema, locale: 'es', },}));</script>Cuando props.role cambie, useForm reconstruye el controller con el nuevo schema. Los valores existentes se preservan en la medida que sean compatibles con el nuevo schema.
Initial values
Sección titulada «Initial values»const form = useForm(() => ({ schema: { modelSchema: userSchema, locale: 'es' }, initialValues: { email: 'usuario@empresa.com', role: 'admin', newsletter: true, },}));initialValues se mergea con los default del schema. Los valores explícitos siempre ganan sobre los defaults.
isDirty se calcula contra esta combinación (initialValues + defaults). Si haces setValue y luego vuelves al valor inicial, isDirty regresa a false.
<script setup>const form = useForm(() => ({ schema: { modelSchema: userSchema, locale: 'es' }, onSubmit: async (values) => { // Lanzar excepción aborta el submit (isSubmitting vuelve a false) await api.createUser(values); toast.success('Usuario creado'); },}));</script>
<template> <form @submit.prevent="form.submit"> <CoFormRenderer :form="form" /> <co-button label="Crear" variant="primary" type="submit" :disabled="!form.isValid.value || form.isSubmitting.value" /> </form></template>Flujo interno del submit()
Sección titulada «Flujo interno del submit()»touchAll()— marca todos los campos como touched (para mostrar todos los errores)validate()— re-valida con AJV- Si inválido: detiene el submit y deja
isSubmitting = false - Si válido: setea
isSubmitting = true, ejecutaonSubmit(values), espera la promise - Al finalizar (éxito o error):
isSubmitting = false
Subscripción cruda al state
Sección titulada «Subscripción cruda al state»Si necesitas un listener (ej. para logging, telemetría), usa form.controller.value.subscribe:
const unsubscribe = form.controller.value.subscribe((state) => { console.log('form state:', state);});
onUnmounted(unsubscribe);