ValidationService
Panoramica
ValidationService è il motore di validazione di @pzeta/vue-form. Espone metodi statici per validare valori con regole pipe-separated, tradurre le chiavi errore i18n e registrare validatori custom.
Quando usare ValidationService invece di useValidation:
| Caso | Strumento consigliato |
|---|---|
| Validazione reattiva di un campo Vue | useValidation |
| Validazione server-side o in un service | ValidationService |
| Validazione in un test unitario | ValidationService |
| Tradurre errori già ottenuti | ValidationService.translateErrors() |
| Registrare un validatore custom | ValidationService.registerValidator() |
| Validare senza i18n (fallback leggibili) | ValidationService.validateWithFallback() |
API
import { ValidationService } from '@pzeta/vue-form'
Metodi statici
export class ValidationService {
// Valida un valore con regole pipe-separated
static validate(value: unknown, rules: string): ValidationResult
// Valida, traduce e restituisce tutto in un'operazione
static validateAndTranslate(
value: unknown,
rules: string,
t: TranslateFunction,
): TranslatedValidationResult
// Valida con messaggi leggibili senza i18n
static validateWithFallback(value: unknown, rules: string): TranslatedValidationResult
// Traduce una singola chiave errore
static translateError(error: string, t: TranslateFunction): string
// Traduce un array di chiavi errore
static translateErrors(errors: string[], t: TranslateFunction): string[]
// Estrae il messaggio fallback da una chiave i18n (senza t())
static extractFallback(key: string): string
// Registra un validatore custom
static registerValidator(name: string, validator: Validator): void
// Lista tutti i validatori disponibili
static getAvailableValidators(): string[]
}
Interfacce
export interface ValidationResult {
isValid: boolean
errors: string[] // chiavi i18n (es. "validation.core.required")
}
export interface TranslatedValidationResult extends ValidationResult {
translatedErrors: string[] // messaggi tradotti/leggibili
}
export type TranslateFunction = (
key: string,
params?: Record<string, string | number>,
) => string
Sintassi regole pipe-separated
Le regole vengono passate come stringa con | come separatore. Vengono eseguite in sequenza; la validazione si ferma al primo errore per regola.
'required|email'
'required|min:8|max:100'
'required|integer|between:1,100'
'required|codicefiscale'
Regole con parametri
| Formato | Esempio | Descrizione |
|---|---|---|
regola | required | Senza parametri |
regola:valore | min:5 | Un parametro numerico |
regola:val1,val2 | between:1,100 | Due parametri |
regola:stringa | pattern:[A-Z]+ | Parametro stringa |
| nei parametri di pattern, regex ed enum
I parametri di pattern, regex ed enum possono contenere |, che resta parte del parametro:
'required|pattern:^(cat|dog)$' // alternanza nella regex
'enum:a|b,c' // valori ammessi: "a|b" e "c"
Dopo queste tre regole un segmento che è il nome di un validatore registrato apre comunque una nuova regola (pattern:^(cat|dog)$|required). Dopo ogni altra regola il | apre sempre una nuova regola: i validatori registrati con registerValidator ricevono il parametro fino al | successivo, quindi per un parametro che contiene | si usano pattern, regex o enum. Il parametro di regex e pattern è il pattern senza delimitatori: regex:^[0-9a-f]{6}$, non regex:/^[0-9a-f]{6}$/.
Regole sconosciute
Dalla 2.12.0 un nome di regola non registrato è un validatore sconosciuto in qualsiasi posizione della stringa, non solo in testa:
| Ambiente | Risultato di validate() |
|---|---|
Node con NODE_ENV=development (test, SSR) | lancia Error: ValidationService: validatore "<nome>" non trovato |
| Altrove (browser, build di produzione) | { isValid: false, errors: ['validation.core.unknownValidator|name:<nome>'] }, tradotto in Validatore sconosciuto: <nome> |
ValidationService.validate('mario@rossi.it', 'required|emial')
// browser / produzione: { isValid: false, errors: ['validation.core.unknownValidator|name:emial'] }
// Node con NODE_ENV=development: Error: ValidationService: validatore "emial" non trovato
Fino alla 2.11.x una regola sconosciuta dopo la prima posizione veniva ignorata senza segnalazione: un refuso come required|emial disattivava il controllo dell'email, e lo stesso accadeva con required: true e una validation sconosciuta. Dalla 2.12.0 lo stesso refuso rende il campo sempre non valido, quindi un esempio copiato con un nome inesistente si nota subito.
Nomi diffusi in altre librerie di validazione che @pzeta/vue-form non registra, con l'equivalente:
| Non esiste | Usa invece |
|---|---|
alphanum | alphanumeric |
same:campo, confirmed | regola cross-field match nella prop validationRules di DynamicFormLayouts |
accepted | required|enum:true: servono entrambe, perché required da solo accetta false ed enum da solo accetta il valore vuoto |
date_before:today, date_after:today | before:today, after:today |
passport | passportnumber |
plate:IT | vehicleregistration:IT |
Per verificare un nome prima di usarlo c'è ValidationService.getAvailableValidators(): 83 validatori built-in, più quelli registrati con registerValidator.
Validatori disponibili (selezione)
| Regola | Parametri | Descrizione |
|---|---|---|
required | — | Campo obbligatorio |
min | :n | Minimo n caratteri (stringa) o valore n (numero) |
max | :n | Massimo n caratteri o valore |
length | :n | Lunghezza esatta |
between | :min,max | Valore compreso tra min e max |
email | — | Formato email valido |
url | — | URL valido |
integer | — | Numero intero |
numeric | — | Valore numerico |
positive | — | Numero positivo |
alpha | — | Solo lettere |
alphanumeric | — | Lettere e numeri |
codicefiscale | — | Codice fiscale italiano |
partitaiva | — | Partita IVA italiana |
iban | — | IBAN valido |
password | — | Password con requisiti standard |
strong | — | Password forte (12+ caratteri, tutti i tipi) |
uuid | — | UUID valido |
enum | :v1,v2,... | Valore tra quelli specificati |
regex | :pattern | Match espressione regolare |
Per la lista completa usare ValidationService.getAvailableValidators().
Flusso traduzione errori
validate() restituisce sempre chiavi i18n grezze. Il formato è:
"validation.categoria.chiave" // senza parametri
"validation.core.min|min:5" // con un parametro
"validation.core.between|min:1,max:100" // con più parametri
La parte dopo il | contiene i parametri nel formato nome:valore separati da virgola. translateError() e translateErrors() li estraggono automaticamente e li passano alla funzione t().
Metodi dettagliati
validate()
Valida e restituisce chiavi i18n grezze. Da usare quando si vuole gestire la traduzione separatamente.
const result = ValidationService.validate('test@example.com', 'required|email')
// { isValid: true, errors: [] }
const invalid = ValidationService.validate('', 'required|email')
// { isValid: false, errors: ['validation.core.required'] }
const tooShort = ValidationService.validate('ab', 'required|min:5')
// { isValid: false, errors: ['validation.core.minString|min:5'] }
validateAndTranslate()
Valida e traduce in un'unica operazione. Richiede la funzione t di @pzeta/vue-i18n.
import { useI18n } from '@pzeta/vue-i18n'
const { t } = useI18n()
const result = ValidationService.validateAndTranslate(
'',
'required|email',
t,
)
// {
// isValid: false,
// errors: ['validation.core.required'],
// translatedErrors: ['Questo campo è obbligatorio'],
// }
validateWithFallback()
Valida con messaggi leggibili senza bisogno di t(). Utile in contesti server-side, test o log.
const result = ValidationService.validateWithFallback('', 'required|min:8')
// {
// isValid: false,
// errors: ['validation.core.required'],
// translatedErrors: ['Required'],
// }
translateError() e translateErrors()
Traducono chiavi già ottenute da validate(), estraendo automaticamente i parametri.
const { t } = useI18n()
// Singolo errore con parametro
const msg = ValidationService.translateError('validation.core.minString|min:5', t)
// "Il campo deve contenere almeno 5 caratteri"
// Array di errori
const msgs = ValidationService.translateErrors(
['validation.core.required', 'validation.format.email'],
t,
)
// ['Questo campo è obbligatorio', 'Inserisci un indirizzo email valido']
extractFallback()
Estrae un messaggio di fallback leggibile da una chiave, senza t().
ValidationService.extractFallback('validation.core.required')
// "Required"
ValidationService.extractFallback('validation.core.minString|min:5')
// "Min 5"
registerValidator()
Registra un validatore custom con un nome che può essere usato nelle regole pipe-separated. La forma del Validator è { fn: ValidatorFn, parameterized: boolean } dove fn ritorna un ValidationResult (non true | string).
import { ValidationService } from '@pzeta/vue-form'
import type { Validator } from '@pzeta/vue-form'
// Validatore senza parametri
const codiceBudget: Validator = {
fn: (value: unknown) => {
const isValid = typeof value === 'string' && /^BDG-\d{4}-[A-Z]{2}$/.test(value)
return {
isValid,
errors: isValid ? [] : ['validation.custom.codiceBudget'],
}
},
parameterized: false,
}
ValidationService.registerValidator('codiceBudget', codiceBudget)
// Validatore con parametro (sintassi `nome:parametro`)
ValidationService.registerValidator('divisibleBy', {
fn: (value, divisor) => {
const num = Number(value)
const div = Number(divisor)
const isValid = !Number.isNaN(num) && !Number.isNaN(div) && num % div === 0
return {
isValid,
errors: isValid ? [] : [`validation.custom.divisibleBy|divisor:${divisor ?? ''}`],
}
},
parameterized: true,
})
// Ora disponibili nelle regole
ValidationService.validate('BDG-2024-AB', 'required|codiceBudget')
ValidationService.validate(15, 'divisibleBy:5')
skipEmpty: per validatori opzionali (che devono passare con valori null / undefined / ''), usa l'helper esportato skipEmpty(value) all'inizio di fn. Convenzione: solo required valida i valori vuoti — gli altri validatori li trattano come "saltabili".getAvailableValidators()
Restituisce la lista di tutti i validatori registrati (built-in + custom).
const validatori = ValidationService.getAvailableValidators()
console.log(validatori)
// ['required', 'min', 'max', 'email', 'url', 'integer', ..., 'codiceBudget']
Esempi completi
Validazione on-submit con traduzione
import { ValidationService } from '@pzeta/vue-form'
import { useI18n } from '@pzeta/vue-i18n'
const { t } = useI18n()
const validateForm = (formData: { email: string; password: string }) => {
const emailResult = ValidationService.validateAndTranslate(
formData.email,
'required|email',
t,
)
const passwordResult = ValidationService.validateAndTranslate(
formData.password,
'required|min:8|password',
t,
)
return {
isValid: emailResult.isValid && passwordResult.isValid,
errors: {
email: emailResult.translatedErrors,
password: passwordResult.translatedErrors,
},
}
}
Validazione in un service backend (senza i18n)
import { ValidationService } from '@pzeta/vue-form'
export class AnagraficaService {
validateInput(data: { codiceFiscale: string; email: string }) {
const cfResult = ValidationService.validateWithFallback(
data.codiceFiscale,
'required|codicefiscale',
)
const emailResult = ValidationService.validateWithFallback(
data.email,
'required|email',
)
if (!cfResult.isValid || !emailResult.isValid) {
throw new Error([
...cfResult.translatedErrors,
...emailResult.translatedErrors,
].join(', '))
}
}
}
Validatore custom con messaggi i18n
Per supportare traduzioni, il validatore deve restituire chiavi i18n registrate nel dizionario dell'app:
// Registrazione validatore (forma { fn, parameterized })
ValidationService.registerValidator('partitaIvaOrCf', {
fn: (value: unknown) => {
if (typeof value !== 'string' || value.trim() === '') {
return { isValid: false, errors: ['validation.core.required'] }
}
const cfResult = ValidationService.validate(value, 'codicefiscale')
const pivaResult = ValidationService.validate(value, 'partitaiva')
const isValid = cfResult.isValid || pivaResult.isValid
return {
isValid,
errors: isValid ? [] : ['validation.italian.cfOrPiva'],
}
},
parameterized: false,
})
// Uso nelle regole
ValidationService.validate('RSSMRA80A01H501U', 'required|partitaIvaOrCf')
// { isValid: true, errors: [] }
Note
- I validatori custom vengono aggiunti al registro globale: registrarli una volta al bootstrap dell'app (es. in
main.ts). validateWithFallback()usaextractFallback()internamente: il messaggio di fallback è derivato dal nome della chiave, non dal dizionario italiano. Per messaggi precisi usare semprevalidateAndTranslate().- Il formato dei parametri nelle chiavi errore (
"chiave|param:val") è stabile e parte dell'API pubblica.