Initialiser un formulaire
L'initialisation d'un formulaire se fait en deux temps :
- vous fabriquez une fonction
useFormaveccreateForm(...); - vous passez à cette fonction un
FormFieldracine, 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
FormFieldracine 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.
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.defaultValuepour créercurrentValue; - instancie le
FormFieldracine et tous ses enfants ; - construit un composant Vue prêt à rendre le template de formulaire ;
- vous retourne
check,reset,disposeetcurrentValue.
Cela implique une distinction essentielle :
currentValuerepré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.
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.
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 :
firstNameetagedéfinissent la forme decurrentValue;- le
dataParsersuragedéfinit la forme de la valeur retournée parcheck(); - le type
ProfileFormSubmitValuepeut être dérivé automatiquement à partir decheck.
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.
<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 :
@submitdéclenche explicitementcheckProfileForm();currentProfileValuepermet 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: unRefsur la valeur courante brute.check(): valide tout l'arbre et retourne soit les erreurs, soit la valeur de sortie.reset(): remet le formulaire à partir desdefaultValue.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
FormFieldracine ; - les layouts servent seulement à composer ce
FormFieldracine ; currentValuemontre l'état vivant du formulaire ;check()produit la valeur métier vérifiée ;- les templates définissent le rendu, pas la structure.
