Skip to Content

Formulaires

  • Utilisez React Hook Form  avec Zod  pour la validation des formulaires, en ajoutant d’abord react-hook-form, @hookform/resolvers et zod aux dépendances du package si le module ne les déclare pas déjà. Cette combinaison offre une validation type-safe et d’excellentes performances :

    import { useForm, Controller } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; // Définir le schéma de validation const formSchema = z.object({ name: z.string({ required_error: t('nameRequired', 'Le nom est requis'), }).min(1, t('nameRequired', 'Le nom est requis')), email: z.string().email(t('invalidEmail', 'Adresse e-mail invalide')), age: z.number().min(18, t('ageMustBe18', 'L'âge doit être d'au moins 18 ans')), password: z.string().min(8, t('passwordTooShort', 'Le mot de passe doit contenir au moins 8 caractères')), confirmPassword: z.string(), }).refine((data) => data.password === data.confirmPassword, { message: t('passwordsDoNotMatch', 'Les mots de passe ne correspondent pas'), path: ['confirmPassword'], }); // Utiliser dans le composant const { handleSubmit, control, formState: { errors }, } = useForm({ resolver: zodResolver(formSchema), defaultValues: { name: '', email: '', age: 18, password: '', confirmPassword: '', }, }); const onSubmit = async (data: z.infer<typeof formSchema>) => { try { await saveData(data); showSnackbar({ title: t('saved', 'Enregistré avec succès') }); } catch (error) { showSnackbar({ kind: 'error', title: t('errorSaving', 'Erreur lors de l'enregistrement'), subtitle: error instanceof Error ? error.message : t('unknownError', 'Erreur inconnue'), }); } };
  • Utilisez Controller de React Hook Form lors de l’intégration avec les composants Carbon qui ne suivent pas les modèles d’entrée HTML standard :

    <Controller name="status" control={control} render={({ field: { onBlur, onChange, value } }) => ( <ComboBox id="status" items={statusOptions} itemToString={(item) => item?.label ?? ''} onBlur={onBlur} onChange={({ selectedItem }) => onChange(selectedItem)} selectedItem={value} titleText={t('status', 'Statut')} /> )} />
  • Gérez les erreurs de soumission de formulaire de manière appropriée. Affichez les erreurs de validation inline et montrez des snackbars pour les erreurs serveur :

    const onSubmit = async (data: FormData) => { try { setIsSubmitting(true); await saveFormData(data); await closeWorkspace({ discardUnsavedChanges: true }); showSnackbar({ title: t('formSaved', 'Formulaire enregistré avec succès') }); } catch (error) { // Erreurs serveur affichées via snackbar showSnackbar({ kind: 'error', title: t('errorSavingForm', 'Erreur lors de l'enregistrement du formulaire'), subtitle: error instanceof Error ? error.message : t('unknownError', 'Erreur inconnue'), }); } finally { setIsSubmitting(false); } };
  • Utilisez les états de chargement pendant la soumission du formulaire pour éviter les soumissions en double :

    const [isSubmitting, setIsSubmitting] = useState(false); <Button type="submit" disabled={isSubmitting} > {isSubmitting ? <InlineLoading description={t('saving', 'Enregistrement...')} /> : t('save', 'Enregistrer')} </Button>
  • Lors de la création de schémas Zod qui incluent des messages d’erreur traduits, créez le schéma en utilisant une fonction qui accepte la fonction de traduction comme paramètre. Cela garantit que les schémas sont correctement localisés et peuvent être mémorisés :

    import type { TFunction } from 'i18next'; // Bon - fonction factory de schéma const createFormSchema = (t: TFunction) => z.object({ name: z.string().min(1, t('nameRequired', 'Le nom est requis')), email: z.string().email(t('invalidEmail', 'Adresse e-mail invalide')), }); // Utiliser dans le composant avec useMemo const { t } = useTranslation(); const formSchema = useMemo(() => createFormSchema(t), [t]); const { control, handleSubmit } = useForm({ resolver: zodResolver(formSchema), });
  • Pour les formulaires Workspace v2 dans le dossier patient, utilisez PatientWorkspace2DefinitionProps de @openmrs/esm-patient-common-lib et enveloppez le contenu du formulaire dans Workspace2 :

    import { Workspace2 } from '@openmrs/esm-framework'; import { type PatientWorkspace2DefinitionProps } from '@openmrs/esm-patient-common-lib'; interface MyFormProps { encounterUuid?: string; } export default function MyForm({ closeWorkspace, workspaceProps, groupProps, }: PatientWorkspace2DefinitionProps<MyFormProps, object>) { const patientUuid = groupProps?.patientUuid; const encounterUuid = workspaceProps?.encounterUuid; const { formState: { isDirty }, } = useForm(); return ( <Workspace2 title={t('myForm', 'Mon formulaire')} hasUnsavedChanges={isDirty}> {/* Utilisez patientUuid et encounterUuid pour afficher le formulaire */} </Workspace2> ); }
  • Utilisez la prop hasUnsavedChanges de Workspace2 pour avertir les utilisateurs lorsqu’ils tentent de fermer un formulaire d’espace de travail avec des modifications non enregistrées :

    const { formState: { isDirty }, } = useForm(...); return ( <Workspace2 title={t('editItem', "Modifier l'élément")} hasUnsavedChanges={isDirty}> {/* Contenu du formulaire */} </Workspace2> );
  • Utilisez closeWorkspace({ discardUnsavedChanges: true }) après une soumission réussie, et closeWorkspace() lorsque l’utilisateur annule :

    const onSubmit = async (data: FormData) => { try { await saveData(data); await mutate(); // Mettre à jour le cache SWR await closeWorkspace({ discardUnsavedChanges: true }); showSnackbar({ title: t('saved', 'Enregistré avec succès') }); } catch (error) { showSnackbar({ kind: 'error', title: t('errorSaving', 'Erreur lors de l'enregistrement'), subtitle: error instanceof Error ? error.message : t('unknownError', 'Erreur inconnue'), }); } }; // Bouton d'annulation <Button kind="secondary" onClick={() => closeWorkspace()}> {t('cancel', 'Annuler')} </Button>
  • Affichez les erreurs au niveau du formulaire en utilisant le composant InlineNotification de Carbon. Envisagez des états d’erreur séparés pour les opérations de création vs mise à jour :

    const [errorCreating, setErrorCreating] = useState<Error | null>(null); const [errorUpdating, setErrorUpdating] = useState<Error | null>(null); {errorCreating && ( <InlineNotification role="alert" kind="error" lowContrast title={t('errorCreating', 'Erreur lors de la création')} subtitle={errorCreating.message} /> )} {errorUpdating && ( <InlineNotification role="alert" kind="error" lowContrast title={t('errorUpdating', 'Erreur lors de la mise à jour')} subtitle={errorUpdating.message} /> )}
Dernière mise à jour le