Skip to content

CoFormRenderer

This content is not available in your language yet.

CoFormRenderer toma el objeto que devuelve useForm() y renderiza todos los campos automáticamente. Para casos típicos no necesitas escribir un solo <co-input> a mano.

<script setup>
import { useForm, CoFormRenderer } from '@prolibu-suite/cobalt-form-vue';
const form = useForm(() => ({
schema: { modelSchema: userSchema, locale: 'es' },
}));
</script>
<template>
<CoFormRenderer :form="form" />
</template>
PropTipoDefaultDescripción
formUseFormReturnObjeto retornado por useForm(). Requerido.
layout'grid' | 'stack''grid'Layout de los campos
columnsnumber2Nº de columnas del grid (stack fuerza 1)
disabledbooleanfalseDeshabilita todos los campos
excludeFieldsstring[][]Nombres de campos a NO renderizar (siguen validando)
fieldComponentsRecord<string, Component>Override de renderers (ver Custom fields)
refResolverRefResolverCallback para campos ref (ver Ref fields)
EventoPayloadCuándo
submit{ values: Record<string, any> }Submit exitoso (válido)
invalidRecord<string, string[]> (el mapa de errores)Submit con errores
change{ name: string; value: any }Cada cambio de campo
<template>
<CoFormRenderer
:form="form"
@submit="onSubmit"
@invalid="onInvalid"
@change="onChange"
/>
</template>
<script setup>
const onSubmit = ({ values }) => {
console.log('válido, valores:', values);
};
const onInvalid = (errors) => {
console.warn('inválido:', errors);
};
const onChange = ({ name, value }) => {
console.log(`${name} →`, value);
};
</script>

CSS Grid responsive de columns columnas (default 2). Cada campo ocupa 1 columna; si el field schema tiene full: true, ocupa la fila completa. Ajustá el número de columnas con la prop columns.

const schema = {
firstName: { type: 'String' }, // 1 columna
lastName: { type: 'String' }, // 1 columna (al lado)
bio: { type: 'String', full: true }, // fila completa
};

Una columna, un campo por fila. Útil para forms angostos (drawers, modales).

<CoFormRenderer :form="form" layout="stack" />

CoFormRenderer expone slots para personalizar zonas específicas.

<CoFormRenderer :form="form">
<template #header>
<h2>Crear usuario</h2>
<p>Completa los datos para registrarte.</p>
</template>
<template #footer>
<div class="actions">
<co-button label="Cancelar" variant="ghost" @click="onCancel" />
<co-button
label="Guardar"
variant="primary"
:disabled="!form.isValid.value"
@click="form.submit"
/>
</div>
</template>
</CoFormRenderer>

Si no defines #footer, se renderiza un botón “Enviar” por defecto.

field:<name> — override de un campo específico

Sección titulada «field:<name> — override de un campo específico»

Para cambiar cómo se renderiza UN campo sin tocar los demás:

<CoFormRenderer :form="form">
<template #field:password="{ field, value, setValue, errors }">
<co-input
:label="field.label"
type="password"
:value="value"
:status="errors.length ? 'error' : undefined"
:helper-text="errors[0] || 'Mín. 8 caracteres'"
@co-input="(e) => setValue(e.detail.value)"
>
<button slot="action" @click="togglePasswordVisibility">👁</button>
</co-input>
</template>
</CoFormRenderer>

El slot recibe:

VariableTipoDescripción
fieldFieldDescriptorMetadata del campo (name, label, kind, etc.)
valueanyValor actual
errorsstring[]Errores del campo (vacío si aún no está touched)
setValue(v) => voidSetter
touch() => voidMarca el campo como touched

A veces quieres validar un campo (porque la API lo requiere) pero no mostrarlo:

<CoFormRenderer
:form="form"
:exclude-fields="['internalId', 'createdAt']"
/>

Los campos excluidos:

  • NO se renderizan en la UI
  • SÍ participan de validación
  • SÍ están en form.values.value (con su default)

Diferente a poner hidden: true en el schema: ese marca a nivel schema; excludeFields es por instancia de renderer.

<!-- form completo deshabilitado durante un loading -->
<CoFormRenderer :form="form" :disabled="isLoading" />

Tiene precedencia sobre el disabled individual de cada campo en el schema.

<script setup>
import { useForm, CoFormRenderer } from '@prolibu-suite/cobalt-form-vue';
import { ref } from 'vue';
import { useToast } from '@/composables/useToast';
const toast = useToast();
const isSaving = ref(false);
const schema = {
firstName: { type: 'String', required: true, minLength: 2, label: 'Nombre' },
lastName: { type: 'String', required: true, minLength: 2, label: 'Apellido' },
email: { type: 'String', required: true, format: 'email', label: 'Email' },
role: {
type: 'String',
enum: ['admin', 'user', 'viewer'],
default: 'user',
label: 'Rol',
},
bio: {
type: 'String',
uiCom: 'TextArea',
maxLength: 500,
full: true,
label: 'Biografía',
},
};
const form = useForm(() => ({
schema: { modelSchema: schema, locale: 'es' },
}));
// El evento @submit del renderer solo dispara con valores válidos
async function onSubmit({ values }) {
isSaving.value = true;
try {
await api.createUser(values);
toast.success('Usuario creado');
} finally {
isSaving.value = false;
}
}
</script>
<template>
<CoFormRenderer
:form="form"
layout="grid"
@submit="onSubmit"
@invalid="() => toast.error('Revisa los errores marcados')"
>
<template #header>
<h2>Nuevo usuario</h2>
</template>
<template #footer="{ submit }">
<co-button label="Cancelar" variant="ghost" @click="$emit('cancel')" />
<co-button
label="Crear usuario"
variant="primary"
:disabled="!form.isValid.value || isSaving"
@click="submit"
/>
</template>
</CoFormRenderer>
</template>