useFieldValidation
Panoramica
useFieldValidation gestisce la validazione di campi multipli all'interno di un form. È il composable usato internamente da FormSectionRow (e quindi da DynamicFormLayouts) per ottenere il comportamento "validazione al blur con messaggio inline".
A differenza di useValidation che lavora su un singolo valore, useFieldValidation:
- Mantiene una mappa
fieldErrors[fieldName] = string[]per tutti i campi del form - Espone
handleBlur(field)da agganciare al@blurdel componente input - Traduce le chiavi i18n usando una funzione
tpassata dall'esterno (decoupling da@pzeta/vue-i18n)
API
import { useFieldValidation } from '@pzeta/vue-form'
import type { FieldDefinition } from '@pzeta/vue-form'
type TranslateFn = (key: string, params?: Record<string, string | number>) => string
export function useFieldValidation(
formDataSource: Record<string, unknown> | (() => Record<string, unknown>),
t: TranslateFn,
): {
fieldErrors: Record<string, string[]>
validateField: (field: FieldDefinition) => boolean
isFieldValid: (field: FieldDefinition) => boolean
handleBlur: (field: FieldDefinition) => void
hasFieldError: (fieldName: string) => boolean
getFieldError: (fieldName: string) => string | undefined
}
Parametri
| Parametro | Tipo | Descrizione |
|---|---|---|
formDataSource | Record<string, unknown> | (() => Record<string, unknown>) | Dati reattivi del form. Accetta sia un oggetto reattivo direttamente, sia un getter () => Record<string, unknown>. Il getter è la forma corretta quando il caller riceve formData come prop: il parent può sostituire l'oggetto sottostante (re-init / reset / cambio schema) e senza getter la closure cattura il riferimento stantio leggendo sempre i valori iniziali |
t | TranslateFn | Funzione di traduzione i18n — tipicamente t di useI18n() di @pzeta/vue-i18n |
Valore di ritorno
| Proprietà | Tipo | Descrizione |
|---|---|---|
fieldErrors | Record<string, string[]> | Mappa reattiva degli errori per campo (chiavi i18n raw, non tradotte) |
validateField | (field) => boolean | Valida un singolo campo, popola fieldErrors[field.name] e ritorna true se valido. Per campi con transform.read, valida il risultato di transform.read(formData) invece di formData[field.name] |
isFieldValid | (field) => boolean | Validazione silenziosa: ritorna true/false senza popolare fieldErrors. Usata per calcolare la validità reattiva del form (es. abilitazione del pulsante "Crea") senza forzare la visualizzazione degli errori prima dell'interazione utente |
handleBlur | (field) => void | Wrapper di validateField da agganciare a @blur |
hasFieldError | (fieldName) => boolean | true se il campo ha almeno un errore |
getFieldError | (fieldName) => string | undefined | Primo errore del campo già tradotto tramite t() |
validateField è "rumoroso" (popola fieldErrors → l'errore appare in UI), isFieldValid è "silenzioso" (solo ritorno booleano). Il pattern usato da DynamicFormLayouts è:isFieldValidper calcolare lo statoupdate:validaggregato (silent — il pulsante save si abilita/disabilita senza mostrare errori)validateFieldsolo on blur (in modalità edit) o on submit (validate())
Esempi
Uso diretto in un componente custom
Se stai costruendo un form custom (non DynamicFormLayouts) e vuoi la stessa logica di validazione on-blur:
<script setup lang="ts">
import { reactive } from 'vue'
import { useFieldValidation } from '@pzeta/vue-form'
import { useI18n } from '@pzeta/vue-i18n'
import { InputText } from '@pzeta/vue-components'
import type { FieldDefinition } from '@pzeta/vue-form'
const { t } = useI18n()
const formData = reactive<Record<string, unknown>>({
nome: '',
email: ''
})
const schema: FieldDefinition[] = [
{ name: 'nome', type: 'text', label: 'Nome', validation: 'required|min:2', required: true },
{ name: 'email', type: 'text', label: 'Email', validation: 'required|email', required: true }
]
const { hasFieldError, getFieldError, handleBlur, validateField } = useFieldValidation(formData, t)
function handleSubmit() {
const allValid = schema.every(field => validateField(field))
if (allValid) {
// Procedi con submit
console.log('Submit:', formData)
}
}
</script>
<template>
<form @submit.prevent="handleSubmit">
<InputText
v-for="field in schema"
:key="field.name"
v-model="formData[field.name]"
:label="field.label"
:required="field.required"
:invalid="hasFieldError(field.name)"
:error="getFieldError(field.name)"
@blur="handleBlur(field)"
/>
<button type="submit">Salva</button>
</form>
</template>
Validazione manuale on-submit
Quando l'utente clicca "Salva", validi tutti i campi insieme:
function validateAll(fields: FieldDefinition[]): boolean {
return fields.every(field => validateField(field))
}
Dopo la chiamata, fieldErrors contiene gli errori di tutti i campi e l'UI può mostrarli inline grazie a getFieldError.
Reset errori
import { useFieldValidation } from '@pzeta/vue-form'
const { fieldErrors } = useFieldValidation(formData, t)
function resetErrori() {
Object.keys(fieldErrors).forEach(key => {
fieldErrors[key] = []
})
}
Comportamento di validateField
- Legge il valore UI del campo:
- Se
field.transform?.readè definito:field.transform.read(formData)(campo virtuale) - Altrimenti:
formData[field.name]
- Se
- Combina automaticamente
requiredconfield.validationsefield.required === truee la regolarequirednon è già presente - Chiama
ValidationService.validate(value, allRules)diValidationService - Aggiorna
fieldErrors[field.name]con l'array di chiavi errore - Ritorna
result.isValid
Differenza con useValidation
| Aspetto | useValidation | useFieldValidation |
|---|---|---|
| Scope | Singolo valore (Ref | Form completo (Record con N campi) |
| Watch automatico | ✅ on Ref change | ❌ — solo on-blur via handleBlur |
| Output errori | Ref<string[]> (chiavi raw) | Mappa per campo + getter tradotto via t |
| Traduzione i18n | Esterna (chiamata dall'utente) | Integrata via parametro t |
| Uso tipico | Validazione di campo singolo isolato | Form multi-campo con feedback inline |
Note
- Le chiavi errore in
fieldErrorssono non tradotte (es.validation.core.required|min:3). Il primo errore tradotto si ottiene viagetFieldError(fieldName). - La funzione
tpuò essere qualsiasi implementazione compatibile con la firma(key, params?) => string— non sei obbligato a usare@pzeta/vue-i18n. - Il
formDatadeve essere reattivo. Se ricevuto come prop, passa un getter() => props.formDatainvece dell'oggetto: senza getter la closure cattura il riferimento iniziale e la validazione legge i valori obsoleti dopo un re-init/reset del parent. - Per campi virtuali (con
field.transform.read) la validazione viene applicata al valore calcolato — es. un campo_datetimeiniziovirtuale che combinadatascadenza+orarioinizio, la regolarequiredvaluta il risultato della concatenazione, non i singoli campi sottostanti.
Composables
Composables di @pzeta/vue-form per validazione reattiva, fetch dati remoti e gestione opzioni — useValidation, useFieldValidation, useRemoteFetch, useRemoteSetup, useOptionLabel.
useOptionLabel
Composable per risolvere la label di un'opzione da un oggetto con supporto a campo singolo, template ${campo} e dot-notation per campi annidati.