Services

ValidationService

Servizio statico per la validazione con regole pipe-separated, traduzione errori i18n e registrazione validatori custom.

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:

CasoStrumento consigliato
Validazione reattiva di un campo VueuseValidation
Validazione server-side o in un serviceValidationService
Validazione in un test unitarioValidationService
Tradurre errori già ottenutiValidationService.translateErrors()
Registrare un validatore customValidationService.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

FormatoEsempioDescrizione
regolarequiredSenza parametri
regola:valoremin:5Un parametro numerico
regola:val1,val2between:1,100Due parametri
regola:stringapattern:[A-Z]+Parametro stringa

Validatori disponibili (selezione)

RegolaParametriDescrizione
requiredCampo obbligatorio
min:nMinimo n caratteri (stringa) o valore n (numero)
max:nMassimo n caratteri o valore
length:nLunghezza esatta
between:min,maxValore compreso tra min e max
emailFormato email valido
urlURL valido
integerNumero intero
numericValore numerico
positiveNumero positivo
alphaSolo lettere
alphanumericLettere e numeri
codicefiscaleCodice fiscale italiano
partitaivaPartita IVA italiana
ibanIBAN valido
passwordPassword con requisiti standard
strongPassword forte (12+ caratteri, tutti i tipi)
uuidUUID valido
enum:v1,v2,...Valore tra quelli specificati
regex:patternMatch 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')
Helper 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() usa extractFallback() internamente: il messaggio di fallback è derivato dal nome della chiave, non dal dizionario italiano. Per messaggi precisi usare sempre validateAndTranslate().
  • Il formato dei parametri nelle chiavi errore ("chiave|param:val") è stabile e parte dell'API pubblica.