Exemple complet : workflow Builder + Viewer
Cet exemple montre deux pages :
- une page admin pour construire le formulaire ;
- une page publique pour afficher le formulaire.
1. Composable de stockage partagé
ts
// app/composables/useLocalDynamicFormRepository.ts
import type { FormKitSchemaDefinition } from '@formkit/core'
export type LocalDynamicForm = {
id: string
schema: FormKitSchemaDefinition[]
settings: Record<string, unknown>
updatedAt: string
}
const STORAGE_PREFIX = 'qform-demo:dynamic-form:'
export function useLocalDynamicFormRepository() {
function getStorageKey(id: string) {
return `${STORAGE_PREFIX}${id}`
}
function get(id: string): LocalDynamicForm | null {
if (!import.meta.client) return null
const raw = localStorage.getItem(getStorageKey(id))
if (!raw) return null
return JSON.parse(raw) as LocalDynamicForm
}
function save(document: LocalDynamicForm) {
if (!import.meta.client) return document
localStorage.setItem(getStorageKey(document.id), JSON.stringify(document))
return document
}
return {
get,
save,
}
}2. Page admin builder
vue
<!-- app/pages/admin/forms/contact.vue -->
<script setup lang="ts">
import type { FormKitSchemaDefinition } from '@formkit/core'
import type { LocalDynamicForm } from '~/composables/useLocalDynamicFormRepository'
type FormBuilderValues = Record<string, unknown>
const repository = useLocalDynamicFormRepository()
const formId = 'contact'
const schema = ref<FormKitSchemaDefinition[]>([])
const values = ref<FormBuilderValues>({})
const saved = ref(false)
onMounted(() => {
const document = repository.get(formId)
schema.value = document?.schema || []
})
function handleSave(payload: {
schema: FormKitSchemaDefinition[]
values: FormBuilderValues
settings: Record<string, unknown>
}) {
const document: LocalDynamicForm = {
id: formId,
schema: payload.schema,
settings: payload.settings,
updatedAt: new Date().toISOString(),
}
repository.save(document)
saved.value = true
}
</script>
<template>
<QPage padding>
<QBanner v-if="saved" rounded class="bg-positive text-white q-mb-md">
Formulaire sauvegardé. Ouvre maintenant `/forms/contact`.
</QBanner>
<FormBuilder
builder-id="admin-contact-form"
v-model:schema="schema"
v-model:values="values"
title="Administration du formulaire contact"
autosave
@save="handleSave"
/>
</QPage>
</template>3. Page publique viewer
vue
<!-- app/pages/forms/contact.vue -->
<script setup lang="ts">
import type { FormKitSchemaDefinition } from '@formkit/core'
const repository = useLocalDynamicFormRepository()
const values = ref<Record<string, unknown>>({})
const schema = ref<FormKitSchemaDefinition[]>([])
const submitted = ref(false)
onMounted(() => {
const document = repository.get('contact')
schema.value = document?.schema || []
})
function handleSubmit(data: Record<string, unknown>) {
console.log('Soumission publique', data)
submitted.value = true
}
</script>
<template>
<QPage padding>
<QCard flat bordered class="q-pa-lg">
<QCardSection>
<div class="text-h5">Contact</div>
<div class="text-body2 text-grey-7">
Formulaire généré dynamiquement depuis le builder.
</div>
</QCardSection>
<QBanner v-if="submitted" rounded class="bg-positive text-white q-mb-md">
Formulaire envoyé.
</QBanner>
<QBanner v-if="!schema.length" rounded class="bg-warning text-dark">
Aucun schéma sauvegardé. Construis d’abord le formulaire dans `/admin/forms/contact`.
</QBanner>
<FormViewer
v-else
v-model="values"
:form-fields="schema"
@submit="handleSubmit"
/>
</QCard>
</QPage>
</template>Passage en production
Pour un usage réel, remplace localStorage par :
txt
service NFZ / Feathers
MongoDB
validation Zod côté serveur
RBAC admin
journalisation des versions de schéma