Campos custom
This content is not available in your language yet.
Cobalt cubre los casos comunes (text, number, select, boolean, date, ref, textarea). Para casos específicos del dominio — firma digital, color picker, file uploader, JSON editor, mapa — necesitas un campo custom.
Hay dos formas de hacerlo, según el caso.
Opción 1: uiCom + componente custom (Vue)
Sección titulada «Opción 1: uiCom + componente custom (Vue)»Declara el componente en el schema con uiCom:
const schema = { signature: { type: 'String', uiCom: 'signature', // string libre que identifica tu componente label: 'Firma', required: true, }, accentColor: { type: 'String', uiCom: 'color-picker', default: '#02a270', label: 'Color de marca', },};Crea el componente custom:
<script setup>const props = defineProps({ field: Object, // FieldDescriptor value: String, setValue: Function, error: String, touched: Boolean,});
const canvas = ref(null);const isEmpty = ref(true);
const clear = () => { canvas.value.clear(); props.setValue(''); isEmpty.value = true;};
const save = () => { const dataUrl = canvas.value.toDataURL(); props.setValue(dataUrl); isEmpty.value = false;};</script>
<template> <div class="signature-field"> <label>{{ field.label }}</label> <SignaturePad ref="canvas" @end="save" /> <co-button label="Limpiar" variant="ghost" size="s" @click="clear" /> <p v-if="error && touched" class="error">{{ error }}</p> </div></template>Y pásalo a CoFormRenderer vía fieldComponents:
<script setup>import SignatureField from './SignatureField.vue';import ColorPickerField from './ColorPickerField.vue';
const customFields = { 'signature': SignatureField, 'color-picker': ColorPickerField,};</script>
<template> <CoFormRenderer :form="form" :field-components="customFields" /></template>CoFormRenderer ahora usa tu componente cada vez que encuentra un campo con uiCom: 'signature'.
Opción 2: Slot field:<name> (caso único)
Sección titulada «Opción 2: Slot field:<name> (caso único)»Si solo necesitas customizar UN campo en UN form (no reutilizable), usa el slot directamente:
<CoFormRenderer :form="form"> <template #field:signature="{ value, setValue, error, touched }"> <SignaturePad :value="value" :error="touched && error" @change="setValue" /> </template></CoFormRenderer>No necesitas uiCom en el schema porque el slot machea por nombre del campo, no por kind.
Cuál usar
Sección titulada «Cuál usar»| Caso | Opción recomendada |
|---|---|
| Componente reutilizable en muchos forms | uiCom + fieldComponents |
| Override puntual de UN campo en UN form | Slot field:<name> |
| Mismo tipo de campo en muchos schemas | uiCom + fieldComponents |
| Diferentes overrides en cada form (ej. password con eye toggle solo aquí) | Slot |
Props que recibe un campo custom
Sección titulada «Props que recibe un campo custom»interface FieldRendererProps { field: FieldDescriptor; // metadata del campo desde el schema value: any; // valor actual setValue: (v: any) => void; // setter touch: () => void; // marca como touched error: string | undefined; // primer error (si touched) errors: string[]; // todos los errores touched: boolean; disabled: boolean; // disabled global o del campo readOnly: boolean;}FieldDescriptor te da acceso a TODOS los atributos del schema:
interface FieldDescriptor { name: string; kind: FieldKind; label: string; placeholder: string; required: boolean; disabled: boolean; hidden: boolean; fullWidth: boolean; helperText: string; originalAttrs: Record<string, any>; // ← TU `uiCom`, `description`, etc. ajvProperty: Record<string, any>;}Si pusiste uiCom: 'signature' en el schema, lo encuentras en field.originalAttrs.uiCom.
Ejemplo: file uploader
Sección titulada «Ejemplo: file uploader»<script setup>import { ref } from 'vue';
const props = defineProps({ field: Object, value: Array, // array de URLs subidas setValue: Function, error: String, touched: Boolean,});
const uploading = ref(false);
const onChange = async (e) => { const files = Array.from(e.target.files); uploading.value = true;
const uploaded = await Promise.all( files.map(async (file) => { const formData = new FormData(); formData.append('file', file); const res = await fetch('/api/upload', { method: 'POST', body: formData }); const { url } = await res.json(); return url; }) );
props.setValue([...(props.value || []), ...uploaded]); uploading.value = false;};
const removeAt = (idx) => { props.setValue(props.value.filter((_, i) => i !== idx));};</script>
<template> <div class="file-field"> <label>{{ field.label }}</label> <input type="file" multiple @change="onChange" :disabled="uploading" /> <ul> <li v-for="(url, i) in value" :key="url"> {{ url }} <button @click="removeAt(i)">x</button> </li> </ul> <p v-if="uploading">Subiendo…</p> <p v-if="error && touched" class="error">{{ error }}</p> </div></template>Schema:
{ attachments: { type: 'String', // o 'Mixed', solo importa que AJV lo valide uiCom: 'file-upload', multiple: true, label: 'Adjuntos', },}Y en el form:
<CoFormRenderer :form="form" :field-components="{ 'file-upload': FileUploadField }"/>Custom kinds (override del FieldKind built-in)
Sección titulada «Custom kinds (override del FieldKind built-in)»También puedes reemplazar los kinds estándar pasando la misma key como en FieldKind:
const customFields = { 'text': MyFancyTextField, // reemplaza TODOS los text fields 'date': DatePickerWithPresets,};Esto es útil si quieres un look-and-feel propio diferente al de Cobalt en TODA tu app.
Custom fields en Web Component
Sección titulada «Custom fields en Web Component»Para <co-form> (Web Component), los custom fields se pasan vía slots field:<name>:
<co-form schema='...'> <div slot="field:signature"> <signature-pad id="sig"></signature-pad> </div></co-form>
<script> const form = document.querySelector('co-form'); const pad = document.getElementById('sig');
pad.addEventListener('change', async (e) => { await form.setValue('signature', e.detail.dataUrl); });</script>Es más manual que la versión Vue: tú escuchas el evento del componente custom y propagas el valor al form via setValue().