Skip to content

Validation avancée

QForm Builder expose un contrat de validation sérialisable pour les règles natives FormKit, les expressions régulières contrôlées, les comparaisons entre champs et les règles métier fournies par l’application hôte.

Principe d’architecture

Le schéma sauvegardé conserve uniquement des données JSON :

ts
{
  validation: 'required|multiple_of:5|qform_compare:minimum,gte',
  validationMessages: {
    multiple_of: 'La valeur doit être un multiple de 5.',
  },
}

Les fonctions TypeScript ne sont jamais stockées dans le schéma. Elles sont déclarées dans un registre runtime transmis à FormBuilder et FormViewer avec validation-rules.

Cette séparation permet de sauvegarder, importer et exporter les formulaires sans sérialiser de code exécutable.

Règles intégrées

Le panneau Validation avancée expose notamment :

  • email, url et number ;
  • lettres, alphanumérique, minuscules et majuscules ;
  • présence d’un chiffre, d’une minuscule, d’une majuscule ou d’un symbole ;
  • valeur acceptée, autorisée ou interdite ;
  • préfixe et suffixe ;
  • expression régulière ;
  • comparaison avec un autre champ.

Les contrôles historiques required, minimum, maximum et valeur exacte restent disponibles dans le même panneau.

Expression régulière contrôlée

Le builder stocke une regex dans la règle interne qform_regex :

ts
validation: 'required|qform_regex:%5E[A-Z]%7B3%7D-%5Cd%7B4%7D%24,i'

Le panneau encode automatiquement le motif et les flags. Les protections suivantes sont appliquées avant l’exécution :

  • motif limité à 256 caractères ;
  • flags limités à i, m, s et u ;
  • rejet des backreferences et lookbehinds ;
  • rejet de plusieurs motifs courants de backtracking catastrophique ;
  • compilation préalable avec RegExp.

Ces contrôles réduisent les risques côté navigateur, mais une regex frontend ne remplace jamais une validation serveur.

Validation croisée

La règle qform_compare compare la valeur du champ courant à un autre champ du même formulaire.

Opérateurs disponibles :

OpérateurSignification
eqégal
neqdifférent
gtsupérieur
gtesupérieur ou égal
ltinférieur
lteinférieur ou égal

Exemple sérialisé :

ts
{
  $formkit: 'q-input',
  name: 'confirmation',
  label: 'Confirmation',
  validation: 'required|qform_compare:password,eq',
}

Le moteur utilise le graphe de nœuds FormKit. Lorsque le champ cible change, la règle est recalculée de manière réactive.

Registre public de règles

Déclarez un registre typé dans l’application hôte :

ts
import type { QFormBuilderValidationRuleRegistry } from '@vevedh/qform-builder-layer/types'

export const validationRules: QFormBuilderValidationRuleRegistry = [
  {
    key: 'multiple_of',
    label: { fr: 'Multiple de', en: 'Multiple of' },
    description: {
      fr: 'Vérifie qu’une valeur est un multiple du diviseur.',
      en: 'Checks that a value is a multiple of the divisor.',
    },
    category: 'custom',
    arguments: [
      {
        key: 'divisor',
        label: { fr: 'Diviseur', en: 'Divisor' },
        type: 'number',
        required: true,
        defaultValue: '5',
      },
    ],
    handler: (node, divisor = '1') => {
      const value = Number(node.value)
      const normalizedDivisor = Number(divisor)

      return Number.isFinite(value)
        && Number.isFinite(normalizedDivisor)
        && normalizedDivisor !== 0
        && value % normalizedDivisor === 0
    },
    messages: {
      fr: ({ args }) => `La valeur doit être un multiple de ${args[0] || '1'}.`,
      en: ({ args }) => `The value must be a multiple of ${args[0] || '1'}.`,
    },
  },
]

Une clé de règle doit respecter le format suivant :

text
^[a-z][a-z0-9_-]{0,63}$

Une règle personnalisée peut être synchrone ou retourner une Promise<boolean>.

Utilisation dans le builder et le viewer

Le même registre doit être transmis aux deux composants :

vue
<script setup lang="ts">
import { validationRules } from '~/validation/rules'

const schema = ref([])
const values = ref<Record<string, unknown>>({})
</script>

<template>
  <FormBuilder
    v-model:schema="schema"
    v-model:values="values"
    :validation-rules="validationRules"
  />

  <FormViewer
    v-model="values"
    :form-fields="schema"
    :validation-rules="validationRules"
  />
</template>

Sans le registre runtime, une règle personnalisée présente dans le JSON ne possède aucune fonction à exécuter.

Messages personnalisés

Chaque champ peut remplacer le message par défaut d’une règle :

ts
{
  validation: 'required|multiple_of:5',
  validationMessages: {
    required: 'Ce montant est obligatoire.',
    multiple_of: 'Le montant doit progresser par tranche de 5.',
  },
}

Priorité appliquée :

  1. message défini sur le champ ;
  2. message localisé du registre public ;
  3. message natif FormKit.

Helpers publics

Le sous-chemin validation expose les fonctions de bas niveau :

ts
import {
  createQFormBuilderValidationRuleToken,
  parseQFormBuilderValidation,
  removeQFormBuilderValidationRule,
  resolveQFormBuilderValidationRegistry,
  upsertQFormBuilderValidationRule,
  validateQFormBuilderRegex,
} from '@vevedh/qform-builder-layer/validation'

Utilisez ces helpers plutôt que des recherches partielles dans la chaîne de validation. Par exemple, retirer min ne doit pas supprimer accidentellement une règle contains_minuscule ou une autre clé contenant le même texte.

Sécurité et validation serveur

Le registre est du code de confiance fourni par l’application hôte. Un JSON importé ne peut pas injecter de fonctions validationRules : cette propriété runtime est explicitement rejetée par l’assainisseur d’import.

Le frontend améliore l’UX, mais les données doivent toujours être validées à nouveau côté serveur, idéalement avec un schéma Zod dans le service Feathers/NFZ concerné.

QForm Builder — layer Nuxt 4, Quasar et FormKit pour formulaires dynamiques.