Forms

useSplitAttrs

Composable interno che ripartisce gli attributi non dichiarati come prop tra il wrapper radice del campo e il controllo interno.

Composable interno — utilizzato da tutti i campi form della libreria che dichiarano inheritAttrs: false.

I campi form dichiarano inheritAttrs: false per evitare che gli attributi finiscano sul wrapper anziché sul controllo. Senza ribattere $attrs da nessuna parte, però, li scartavano tutti in silenzio: class, style, data-* e gli attributi ARIA extra sparivano dal DOM. Questo composable, introdotto nella 1.9.0, li riparte fra i due elementi.

Firma TypeScript

export function useSplitAttrs(options?: UseSplitAttrsOptions): UseSplitAttrsResult

export interface UseSplitAttrsOptions {
  exclude?: string[];   // attributi da non ribattere sul controllo interno
}

export interface UseSplitAttrsResult {
  attrs: Record<string, unknown>;
  rootAttrs: ComputedRef<Record<string, unknown>>;
  controlAttrs: ComputedRef<Record<string, unknown>>;
}

Parametri

ParametroTipoDescrizione
options.excludestring[]Attributi aggiuntivi da tenere fuori dal controllo interno, oltre a quelli sempre esclusi

Valore di Ritorno

ProprietàTipoDescrizione
attrsRecord<string, unknown>$attrs grezzi, per leggere singole chiavi (es. attrs.id)
rootAttrsComputedRef<Record<string, unknown>>class e style, da applicare al wrapper radice
controlAttrsComputedRef<Record<string, unknown>>Tutti gli altri attributi, da applicare al controllo interno

Ripartizione

AttributoDestinazione
class, stylerootAttrs → wrapper radice (si fondono con le classi del componente)
data-*, name, aria-* extra, listener non dichiaraticontrolAttrs → controllo interno
id, aria-describedbyEsclusi: già calcolati dal componente

id e aria-describedby sono sempre esclusi perché il componente li costruisce da sé — il primo per l'associazione con la <label>, il secondo concatenando gli id di helper text, messaggio di errore e counter. Un pass-through cieco li sovrascriverebbe, perdendo l'associazione con i messaggi.

Esempio

<script setup lang="ts">
  defineOptions({ inheritAttrs: false })
  import { useSplitAttrs } from '@/composables/form'

  const { attrs, rootAttrs, controlAttrs } = useSplitAttrs()
  const inputId = computed(() => (attrs.id as string) || generatedId.value)
</script>

<template>
  <div :class="wrapperClasses" v-bind="rootAttrs">
    <input v-bind="controlAttrs" :id="inputId" :class="inputClasses" />
  </div>
</template>

L'ordine conta: v-bind="controlAttrs" va prima dei binding espliciti del componente, così in caso di chiavi duplicate vincono questi ultimi.

Campi compositi

Int4RangeInput e IntervalInput non hanno un singolo controllo nativo: controlAttrs viene applicato al gruppo [role="group"] che raccoglie i campi. Nei test il campo interno si raggiunge con [data-testid="..."] input.

Componenti che lo usano

InputText, InputNumber, Password, Textarea, Select, TreeSelect, AutoComplete, DatePicker, DateTimePicker, TimePicker, Int4RangeInput, IntervalInput, SignatureInput.