Skip to content

Modèles de formulaires

La version 0.1.9 ajoute un registre public de modèles prêts à l’emploi. L’utilisateur peut rechercher un modèle depuis le panneau gauche, remplacer le formulaire courant ou ajouter un modèle à la suite du schéma existant.

Les modèles restent des données sérialisables : schéma, valeurs initiales et réglages optionnels. Aucun composant Vue, handler ou code exécutable n’est enregistré dans le JSON.

Modèles Community inclus

CléUsage
simple-contactCoordonnées, objet, message et consentement.
satisfaction-surveyNotation, recommandation, commentaire et contact conditionnel.
admin-crudCréation ou modification d’un compte dans une interface d’administration.
employee-onboardingParcours multi-étapes pour l’identité, les accès, les équipements et la confirmation.

Les titres et descriptions des quatre modèles suivent la locale active de FormBuilder.

Utiliser le navigateur de modèles

Ouvrez le panneau des composants puis l’onglet Modèles. Deux actions sont disponibles :

  • Utiliser remplace le schéma et les valeurs courants après confirmation ;
  • Ajouter conserve le formulaire courant et ajoute les nouveaux champs à la racine.

L’application d’un modèle produit une entrée template-apply dans l’historique. L’annulation restaure le schéma précédent. Les valeurs et réglages ne font pas encore partie de la timeline : une application hôte qui exige une restauration transactionnelle complète doit conserver un snapshot métier côté serveur.

Fournir des modèles personnalisés

vue
<script setup lang="ts">
import type {
  QFormBuilderTemplateApplyEvent,
  QFormBuilderTemplateRegistry,
} from '@vevedh/qform-builder-layer/types'
import { defineQFormTemplate } from '@vevedh/qform-builder-layer/templates'

const templates: QFormBuilderTemplateRegistry = [
  defineQFormTemplate({
    key: 'incident-report',
    title: 'Déclaration d’incident',
    description: 'Collecte les informations nécessaires à la qualification initiale.',
    icon: 'report_problem',
    category: 'security',
    tags: ['incident', 'support', 'sécurité'],
    schema: [
      {
        $formkit: 'q-select',
        name: 'severity',
        label: 'Criticité',
        options: [
          { label: 'Faible', value: 'low' },
          { label: 'Moyenne', value: 'medium' },
          { label: 'Élevée', value: 'high' },
        ],
        validation: 'required',
      },
      {
        $formkit: 'q-input',
        name: 'summary',
        label: 'Résumé',
        validation: 'required|length:10,160',
      },
      {
        $formkit: 'q-input',
        name: 'details',
        label: 'Description détaillée',
        inputType: 'textarea',
        validation: 'required|length:20,4000',
      },
    ],
    values: {
      severity: 'medium',
    },
  }),
]

function handleTemplateApply(event: QFormBuilderTemplateApplyEvent): void {
  console.info(event.builderId, event.template.key, event.mode, event.nameMap)
}
</script>

<template>
  <FormBuilder
    builder-id="incident-builder"
    :templates="templates"
    @template-apply="handleTemplateApply"
  />
</template>

Avec le mode par défaut append, les modèles personnalisés sont fusionnés avec les quatre modèles Community. Une clé personnalisée identique remplace le modèle Community correspondant.

Pour publier uniquement le registre de l’application hôte :

vue
<FormBuilder
  :templates="templates"
  template-mode="replace"
/>

API publique

vue
<script setup lang="ts">
import type {
  FormBuilderPublicApi,
  QFormBuilderTemplateApplyEvent,
} from '@vevedh/qform-builder-layer/types'

const builder = ref<FormBuilderPublicApi | null>(null)

function appendContact(): QFormBuilderTemplateApplyEvent | null {
  return builder.value?.applyTemplate('simple-contact', {
    mode: 'append',
    applyValues: true,
    applySettings: false,
  }) ?? null
}

function replaceWithOnboarding(): QFormBuilderTemplateApplyEvent | null {
  return builder.value?.applyTemplate('employee-onboarding', {
    mode: 'replace',
  }) ?? null
}

function listTemplates(): void {
  const templates = builder.value?.getTemplates() ?? []
  console.table(templates.map(({ key, title, category }) => ({ key, title, category })))
}
</script>

<template>
  <FormBuilder ref="builder" builder-id="templates-api" />
</template>

Méthodes exposées :

ts
getTemplates(): QFormBuilderTemplateRegistry
applyTemplate(
  templateKey: string,
  options?: QFormBuilderTemplateApplyOptions,
): QFormBuilderTemplateApplyEvent | null

L’événement template-apply contient le schéma, les valeurs, les réglages, le mode appliqué et nameMap, qui associe chaque nom source au nom réellement inséré.

Fusion sûre en mode append

Le moteur protège les invariants suivants :

  1. le schéma du modèle est assaini avec le même pipeline que l’import JSON ;
  2. les noms dupliqués à l’intérieur d’un modèle sont refusés ;
  3. les collisions avec le formulaire courant sont suffixées (email, email_2, email_3) ;
  4. les conditions et les règles qform_compare sont réécrites avec les nouveaux noms ;
  5. les valeurs initiales sont remappées ;
  6. l’ajout est refusé dès que le formulaire courant ou le modèle contient un q-stepper racine, sauf lorsque le formulaire courant est vide.

Les objets sources sont clonés avant transformation. Le registre fourni par l’application hôte n’est donc pas muté.

Les métadonnées, valeurs et réglages sont normalisés avant exposition : fonctions, dates, nombres non finis, prototypes personnalisés et clés __proto__ / prototype / constructor sont refusés. Les données sont bornées à 20 niveaux, 5 000 nœuds et 100 000 caractères par chaîne. La largeur d’aperçu est convertie en nombre et limitée à 4 096 px.

Le thème visuel actif reste toujours celui de l’instance FormBuilder : un modèle ne peut pas le remplacer. Seuls formName, previewMode, columns et les réglages d’aperçu reconnus peuvent être importés depuis un modèle.

Helpers purs

ts
import type {
  FormBuilderSchema,
  FormBuilderSettings,
  FormBuilderValues,
} from '@vevedh/qform-builder-layer/types'
import { frQFormBuilderLocale } from '@vevedh/qform-builder-layer/locales'
import {
  applyQFormBuilderTemplate,
  canAppendQFormBuilderTemplate,
  createCommunityTemplates,
} from '@vevedh/qform-builder-layer/templates'

function applyOutsideComponent(
  schema: FormBuilderSchema,
  values: FormBuilderValues,
  settings: FormBuilderSettings,
): FormBuilderSchema {
  const template = createCommunityTemplates(frQFormBuilderLocale)
    .find(candidate => candidate.key === 'simple-contact')

  if (!template || !canAppendQFormBuilderTemplate(schema, template.schema)) {
    return schema
  }

  return applyQFormBuilderTemplate(
    template,
    { schema, values, settings },
    { mode: 'append' },
  ).schema
}

createCommunityTemplates() accepte une locale QForm Builder résolue. Dans une application, préférez le registre retourné par getTemplates() lorsque vous devez respecter les surcharges locales du composant.

Sécurité et validation serveur

Un modèle accélère la conception ; il ne constitue jamais une politique de sécurité. À la soumission :

  • validez à nouveau le payload côté FeathersJS/NFZ avec Zod ;
  • appliquez RBAC/ABAC indépendamment des champs visibles ou masqués ;
  • ignorez les propriétés non autorisées avec un schéma strict ;
  • contrôlez les fichiers, URL, regex et règles asynchrones côté serveur ;
  • versionnez le schéma métier persisté pour rendre les migrations explicites.

Bonnes pratiques

  • Utiliser des clés stables, courtes et versionnables, par exemple incident-report-v2.
  • Conserver des noms de champs indépendants des libellés traduits.
  • Préférer replace pour les workflows structurants et append pour des blocs simples.
  • Limiter les valeurs initiales aux données non sensibles.
  • Tester les modèles personnalisés avec le même locale, le même registre de validation et le même thème que l’application de production.

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