Skip to content

Initialiser un formulaire

L'initialisation d'un formulaire se fait en deux temps :

  1. vous fabriquez une fonction useForm avec createForm(...) ;
  2. vous passez à cette fonction un FormField racine, qui peut être un input simple ou un layout composé.

Le point important de la librairie est là :

  • un input retourne un FormField ;
  • un layout retourne aussi un FormField ;
  • un formulaire n'est donc rien d'autre que l'instanciation d'un FormField racine sur un état Vue.

Autrement dit, createForm ne connaît pas votre schéma métier. Il sait seulement prendre un FormField, cloner sa defaultValue, instancier toute l'arborescence, puis exposer une API de rendu, de validation et de reset.

Initialisation globale

L'étape globale consiste à préparer les templates qui seront utilisés par tous les formulaires.

ts
import "@duplojs/form/vueGrid.css";
import "@duplojs/form/vueDesignSystem.css";

import { 
createForm
} from "@duplojs/form/vue";
import {
templateFormAddButton
,
templateFormNextButton
,
templateFormPreviousButton
,
templateFormRemoveButton
,
templateFormResetButton
,
templateFormSelect
,
} from "@duplojs/form/vueDesignSystem"; import {
createGridTemplates
} from "@duplojs/form/vueGrid";
export const
templatesGrid
=
createGridTemplates
({
repeat
: {
addLabel
: "Add item",
removeLabel
: "Remove item",
addButton
:
templateFormAddButton
,
removeButton
:
templateFormRemoveButton
,
resetButton
:
templateFormResetButton
,
},
step
: {
nextLabel
: "Continue",
previousLabel
: "Back",
resetButton
:
templateFormResetButton
,
nextButton
:
templateFormNextButton
,
previousButton
:
templateFormPreviousButton
,
},
union
: {
selectInputKind
:
templateFormSelect
},
}); export const
useForm
=
createForm
(
templatesGrid
.
useTemplates
());

createForm(templatesGrid.useTemplates()) retourne une fonction useForm déjà configurée.

Cette fonction fermée sur vos templates devient l'entrée unique pour initialiser tous vos formulaires.

INFO

Les templates vueGrid et les composants de vueDesignSystem sont seulement des valeurs par défaut. Vous pouvez remplacer tout ou partie du rendu sans changer la structure de vos formulaires.

Le principe fondamental

Quand vous appelez useForm(rootField), la librairie :

  • clone rootField.defaultValue pour créer currentValue ;
  • instancie le FormField racine et tous ses enfants ;
  • construit un composant Vue prêt à rendre le template de formulaire ;
  • vous retourne check, reset, dispose et currentValue.

Cela implique une distinction essentielle :

  • currentValue représente l'état courant brut du formulaire ;
  • check() retourne la valeur validée et parsée.

Si un input utilise un dataParser, la valeur de currentValue peut encore être brute, alors que la valeur retournée par check() est déjà transformée et contrôlée.

Le champ racine peut être minimal

Le champ racine n'a pas besoin d'être un objet complexe. Un formulaire peut être initialisé directement à partir d'un seul input.

ts
ts
import { 
useTextInput
} from "@duplojs/form/vueDesignSystem";
import {
useForm
} from "./init";
export function
useNewsletterForm
() {
const {
component
,
check
,
currentValue
,
reset
,
dispose
} =
useForm
(
useTextInput
({
label
: "Email",
defaultValue
: "",
}), ); return {
NewsletterForm
:
component
,
checkNewsletterForm
:
check
,
currentNewsletterValue
:
currentValue
,
resetNewsletterForm
:
reset
,
disposeNewsletterForm
:
dispose
,
}; }

Ici, la valeur du formulaire est simplement une string, parce que le champ racine est un useTextInput(...).

Déclarer un formulaire structuré

Dès que vous avez besoin de plusieurs champs, vous composez un FormField racine avec un layout comme useMultiLayout.

ts
ts
import { 
useMultiLayout
, type
GetCheckedValue
} from "@duplojs/form/vue";
import {
useNumberInput
,
useTextInput
} from "@duplojs/form/vueDesignSystem";
import * as
DP
from "@duplojs/utils/dataParser";
import {
useForm
} from "./init";
export function
useProfileForm
() {
const {
component
,
check
,
reset
,
currentValue
,
dispose
} =
useForm
(
useMultiLayout
({
firstName
:
useTextInput
({
label
: "first name",
defaultValue
: "Math",
}),
age
:
useNumberInput
({
label
: "Age",
defaultValue
: 16,
dataParser
:
DP
.
number
()
.
addChecker
(
DP
.
checkerNumberMin
(
18, {
errorMessage
: "You must be at least 18." },
), ), }), }), ); return {
ProfileForm
:
component
,
checkProfileForm
:
check
,
currentProfileValue
:
currentValue
,
resetProfileForm
:
reset
,
disposeProfileForm
:
dispose
,
}; } export type
ProfileFormSubmitValue
=
GetCheckedValue
<
ReturnType
<typeof
useProfileForm
>["checkProfileForm"]
>;

Dans cet exemple :

  • firstName et age définissent la forme de currentValue ;
  • le dataParser sur age définit la forme de la valeur retournée par check() ;
  • le type ProfileFormSubmitValue peut être dérivé automatiquement à partir de check.

Le formulaire n'est donc pas décrit par un schéma séparé. Sa structure et son type sortent directement de l'arbre de FormField que vous composez.

Utiliser le formulaire dans un composant Vue

Le composant retourné par useForm sert de conteneur de rendu. Son slot par défaut correspond à la zone de soumission.

vue
vue
<script setup lang="ts">
import * as EE from "@duplojs/utils/either";
import { unwrap } from "@duplojs/utils";
import { OutlineButton, PrimaryButton } from "@duplojs/form/vueDesignSystem";
import { onBeforeUnmount, ref } from "vue";
import {
	type ProfileFormSubmitValue,
	useProfileForm,
} from "./profileForm";

const {
	ProfileForm,
	checkProfileForm,
	currentProfileValue,
	resetProfileForm,
	disposeProfileForm,
} = useProfileForm();

const submitResult = ref<ProfileFormSubmitValue | null>(null);

function submit() {
	const result = checkProfileForm();

	if (EE.isRight(result)) {
		submitResult.value = unwrap(result);
	}
}

function reset() {
	resetProfileForm();
	submitResult.value = null;
}

onBeforeUnmount(disposeProfileForm);
</script>

<template>
	<ProfileForm @submit="submit">
		<PrimaryButton
			type="submit"
			label="Submit"
		/>

		<OutlineButton
			type="button"
			label="Reset"
			@click="reset"
		/>
	</ProfileForm>

	<pre>currentValue: {{ currentProfileValue }}</pre>

	<pre>checkedValue: {{ submitResult }}</pre>
</template>

Dans cette implémentation :

  • @submit déclenche explicitement checkProfileForm() ;
  • currentProfileValue permet d'observer l'état courant en direct ;
  • resetProfileForm() remet les valeurs par défaut ;
  • disposeProfileForm() est appelé au démontage pour libérer les scopes internes.

Résultat

currentValue: {
  "firstName": "Math",
  "age": 16
}
checkedValue: 

API retournée par useForm

useForm(...) retourne toujours un objet avec les propriétés suivantes :

  • component: le composant Vue à rendre.
  • currentValue: un Ref sur la valeur courante brute.
  • check(): valide tout l'arbre et retourne soit les erreurs, soit la valeur de sortie.
  • reset(): remet le formulaire à partir des defaultValue.
  • dispose(): détruit les effets internes liés à l'instance du formulaire.

Ce qu'il faut retenir

  • le formulaire est initialisé à partir d'un unique FormField racine ;
  • les layouts servent seulement à composer ce FormField racine ;
  • currentValue montre l'état vivant du formulaire ;
  • check() produit la valeur métier vérifiée ;
  • les templates définissent le rendu, pas la structure.

Released under the MIT License.