DéveloppeursDocsFournisseurs
Yeria
Documentation

FormView

Saisie et soumission de données, validation, champs typés.

Description

Le composant FormView sert à créer des formulaires dynamiques dotés de types de champs variés, de règles de validation et d'actions de soumission. Il prend en charge un large éventail de saisies : texte, e-mail, mot de passe, nombre, date, liste déroulante, envoi de fichiers, enregistrement audio et vidéo, coordonnées GPS, et bien d'autres.

Un formulaire est soumis exactement à l'URL de service dont la réponse l'a renvoyé. Le client conserve cette URL, contrôle qu'elle reste dans le périmètre de l'URL de base déclarée par le service, et elle n'est pas configurable depuis la charge utile du formulaire. content.submit ne pilote que l'apparence du bouton et la méthode HTTP.

Description des champs

idstringrequis
Identifiant unique de la vue formulaire
typestringrequis
Toujours "Form"
contentFormContentrequis
Objet de contenu du formulaire
content.titlestringrequis
Titre du formulaire présenté à l'utilisateur
content.introstringoptionnel
Introduction ou consignes affichées au-dessus des champs
content.submitSubmitActionoptionnel
Configuration du bouton de soumission et de la méthode HTTP ; ne peut pas remplacer l'URL de destination
content.submit.textstringrequis*
Libellé du bouton (obligatoire si submit est renseigné)
content.submit.methodHttpMethodoptionnel
Méthode HTTP (valeur par défaut : "POST")
content.submit.confirmMessagestringoptionnel
Message facultatif de la boîte de confirmation
content.fieldsFormField[]requis
Tableau des champs du formulaire (au moins un champ autre qu'un séparateur)
content.fields[].fieldTypestringrequis
Nature du champ : "text", "email", "password", "number", "date", "select", "photo", "file", "audio", "video", "gps", "pluscode", "hidden", "textarea", "phone", "url", "checkbox", "separator"
content.fields[].fieldIdstringrequis
Identifiant unique du champ
content.fields[].fieldLabelstringrequis
Libellé affiché pour le champ
content.fields[].valueunknownoptionnel
Valeur initiale ou pré-remplie
content.fields[].requiredbooleanoptionnel
Indique si le champ est requis
content.fields[].placeholderstringoptionnel
Texte indicatif affiché dans le champ vide
content.fields[].helpTextstringoptionnel
Texte d'aide affiché sous le champ
content.fields[].disabledbooleanoptionnel
Indique si le champ est désactivé
content.fields[].readonlybooleanoptionnel
Indique si le champ est en lecture seule
content.fields[].minnumberoptionnel
Valeur minimale (champs numériques ou dates)
content.fields[].maxnumberoptionnel
Valeur maximale (champs numériques ou dates)
content.fields[].minLengthnumberoptionnel
Longueur minimale de la chaîne
content.fields[].maxLengthnumberoptionnel
Longueur maximale de la chaîne
content.fields[].patternRegExpoptionnel
Expression régulière de validation
content.fields[].optionsArray<{label: string, value: unknown, selected?: boolean}>optionnel
Options des champs liste déroulante, radio ou case à cocher
content.fields[].acceptstring[]optionnel
Formats de fichier acceptés (types MIME)
content.fields[].livebooleanoptionnel
Active la mise à jour continue (champs GPS ou photo)
content.fields[].minDatestringoptionnel
Date minimale (au format AAAA-MM-JJ)
content.fields[].maxDatestringoptionnel
Date maximale (au format AAAA-MM-JJ)
content.fields[].multiplebooleanoptionnel
photo/file/audio/video : autorise plusieurs éléments
content.fields[].maxCountnumberoptionnel
Plafond appliqué lorsque multiple vaut true
content.fields[].maxDurationnumberoptionnel
audio/video : durée maximale d'enregistrement, en secondes. Obligatoire pour video
content.fields[].minDurationnumberoptionnel
audio/video : durée minimale d'enregistrement, en secondes
content.fields[].sourcestringoptionnel
photo/audio/video : "record", "library" ou "both" (par défaut)
content.fields[].qualitystringoptionnel
video uniquement : "low", "medium" (par défaut) ou "high"
content.fields[].maxSizenumberoptionnel
audio/video : taille de fichier maximale acceptée, en octets
processIdstringoptionnel
Identifiant de processus pour les parcours en plusieurs étapes
metadataobjectoptionnel
Métadonnées de la vue (version, createdAt, author, tags)

Méthodes

setIntro(intro: string)this
Définit le texte d'introduction affiché au-dessus des champs
  • intro — Texte d'introduction
setNote(note: string)this
Déprécié. Utilisez plutôt setIntro(). Conservé pour compatibilité ascendante
  • note — Texte de la note
belongsToProcess(processId, options?)this
Rattache le formulaire à un parcours en plusieurs étapes
  • processId — Identifiant du processus
  • options — Options du processus (processName, currentStep, totalSteps, stepName, canGoBack, canSkip)
addField(fieldType, fieldId, fieldLabel, params?)this
Ajoute un champ assorti de sa validation
  • fieldType — Nature du champ
  • fieldId — Identifiant unique du champ
  • fieldLabel — Libellé affiché
  • params — Paramètres du champ
submitButton(text, method?, confirmMessage?)this
Définit le bouton de soumission
  • text — Libellé du bouton
  • method — Méthode HTTP (par défaut : POST)
  • confirmMessage — Confirmation facultative
updateButton(text, confirmMessage?)this
Raccourci pour les opérations PUT
  • text — Libellé du bouton
  • confirmMessage — Confirmation facultative
deleteButton(text, confirmMessage?)this
Raccourci pour les opérations DELETE
  • text — Libellé du bouton
  • confirmMessage — Message de confirmation
addTextField(fieldId, fieldLabel, isRequired?, maxLength?)this
Ajoute un champ de saisie texte
  • fieldId — Identifiant du champ
  • fieldLabel — Libellé
  • isRequired — Indicateur de champ requis
  • maxLength — Longueur maximale
addEmailField(fieldId, fieldLabel, isRequired?)this
Ajoute un champ de saisie e-mail
  • fieldId — Identifiant du champ
  • fieldLabel — Libellé
  • isRequired — Indicateur de champ requis
addPasswordField(fieldId, fieldLabel, minLength?)this
Ajoute un champ mot de passe
  • fieldId — Identifiant du champ
  • fieldLabel — Libellé
  • minLength — Longueur minimale (par défaut : 8)
addNumberField(fieldId, fieldLabel, isRequired?, minVal?, maxVal?)this
Ajoute un champ de saisie numérique
  • fieldId — Identifiant du champ
  • fieldLabel — Libellé
  • isRequired — Indicateur de champ requis
  • minVal — Valeur minimale
  • maxVal — Valeur maximale
addDateField(fieldId, fieldLabel, isRequired?, minDate?, maxDate?)this
Ajoute un champ de saisie date
  • fieldId — Identifiant du champ
  • fieldLabel — Libellé
  • isRequired — Indicateur de champ requis
  • minDate — Date minimale (AAAA-MM-JJ)
  • maxDate — Date maximale (AAAA-MM-JJ)
addSelectField(fieldId, fieldLabel, isRequired?, options)this
Ajoute une liste déroulante
  • fieldId — Identifiant du champ
  • fieldLabel — Libellé
  • isRequired — Indicateur de champ requis
  • options — Tableau de {label, value}
addPhotoField(fieldId, fieldLabel, isRequired?, formats?, live?, options?)this
Ajoute un champ d'envoi de photo
  • fieldId — Identifiant du champ
  • fieldLabel — Libellé
  • isRequired — Indicateur de champ requis
  • formats — Formats acceptés (par défaut : ['jpeg', 'png'])
  • live — Mise à jour continue
  • options{multiple, maxCount, source}
addFileField(fieldId, fieldLabel, isRequired?, formats, options?)this
Ajoute un champ d'envoi de fichier
  • fieldId — Identifiant du champ
  • fieldLabel — Libellé
  • isRequired — Indicateur de champ requis
  • formats — Tableau de types MIME
  • options{multiple, maxCount}
addAudioField(fieldId, fieldLabel, isRequired?, options?)this
Ajoute un champ d'enregistrement vocal
  • fieldId — Identifiant du champ
  • fieldLabel — Libellé
  • isRequired — Indicateur de champ requis
  • options{maxDuration, minDuration, source, multiple, maxCount, maxSize, formats}
addVideoField(fieldId, fieldLabel, isRequired?, options)this
Ajoute un champ d'enregistrement vidéo
  • fieldId — Identifiant du champ
  • fieldLabel — Libellé
  • isRequired — Indicateur de champ requis
  • options{maxDuration (required), minDuration, quality, source, multiple, maxCount, maxSize, formats}
addGPSField(fieldId, fieldLabel, isRequired?, liveData?, config?)this
Ajoute un champ de localisation GPS
  • fieldId — Identifiant du champ
  • fieldLabel — Libellé
  • isRequired — Indicateur de champ requis
  • liveData — Mise à jour continue
  • config — Réglages GPS (altitude, precision)
addPlusCodeField(fieldId, fieldLabel, isRequired?, liveData?)this
Ajoute un champ Plus Code
  • fieldId — Identifiant du champ
  • fieldLabel — Libellé
  • isRequired — Indicateur de champ requis
  • liveData — Mise à jour continue
addHiddenField(fieldId, fieldLabel, value)this
Ajoute un champ caché
  • fieldId — Identifiant du champ
  • fieldLabel — Libellé
  • value — Valeur du champ
addTextAreaField(fieldId, fieldLabel, isRequired?, minLength?, maxLength?)this
Ajoute une zone de texte multiligne
  • fieldId — Identifiant du champ
  • fieldLabel — Libellé
  • isRequired — Indicateur de champ requis
  • minLength — Longueur minimale
  • maxLength — Longueur maximale
addPhoneField(fieldId, fieldLabel, isRequired?)this
Ajoute un champ numéro de téléphone
  • fieldId — Identifiant du champ
  • fieldLabel — Libellé
  • isRequired — Indicateur de champ requis
addURLField(fieldId, fieldLabel, isRequired?)this
Ajoute un champ URL
  • fieldId — Identifiant du champ
  • fieldLabel — Libellé
  • isRequired — Indicateur de champ requis
addCheckboxField(fieldId, fieldLabel, isRequired?)this
Ajoute une case à cocher
  • fieldId — Identifiant du champ
  • fieldLabel — Libellé
  • isRequired — Indicateur de champ requis
addSeparator(fieldId?)this
Ajoute un séparateur visuel pour regrouper les champs (rendu sous forme d'espace ou de trait)
  • fieldId — Identifiant facultatif du champ (généré automatiquement s'il est omis)
injectData(data)Result<void, string[]>
Injecte des données dans les champs existants (gère seul les options des listes déroulantes)
  • data — Objet associant des identifiants de champs à leurs valeurs
setFieldValue(fieldId, value)this
Affecte sa valeur à un champ précis
  • fieldId — Identifiant du champ
  • value — Valeur à affecter
validateFormData(formData)ValidationResult
Contrôle les données saisies au regard des règles de validation des champs
  • formData — Objet contenant les données du formulaire
getField(fieldId)FormField | undefined
Récupère un champ par son identifiant
  • fieldId — Identifiant du champ
removeField(fieldId)Result<void, string>
Supprime un champ par son identifiant
  • fieldId — Identifiant du champ
updateField(fieldId, updates)Result<void, string>
Met à jour un champ existant
  • fieldId — Identifiant du champ
  • updates — Modifications partielles du champ
getFields()FormField[]
Récupère l'ensemble des champs
getFieldCount(excludeSeparators?)number
Renvoie le nombre de champs, séparateurs exclus le cas échéant
  • excludeSeparators — Exclure les séparateurs (par défaut : false)
hasRequiredFields()boolean
Indique si le formulaire comporte des champs requis
getRequiredFields()string[]
Renvoie la liste des identifiants des champs requis
serve()Record<string, unknown>
Sert la vue après validation (hérité de BaseView)
toJSON()Record<string, unknown>
Renvoie la représentation JSON (hérité de BaseView)
setState(key, value)void
Fixe l'état de la vue (hérité de BaseView)
  • key — Clé d'état
  • value — Valeur d'état
getState(key)unknown
Lit l'état de la vue (hérité de BaseView)
  • key — Clé d'état
setNext(url)this
Fixe la navigation vers la vue suivante (hérité de BaseView)
  • url — URL de la vue suivante
setPrev(url)this
Fixe la navigation vers la vue précédente (hérité de BaseView)
  • url — URL de la vue précédente
setProcess(processId, context?)this
Fixe le contexte de processus (hérité de BaseView)
  • processId — Identifiant du processus
  • context — Contexte du processus

Exemples de code JavaScript

Formulaire simple

javascript
1import { YeriaApp } from '@numerum-tech/yeriasdk';
2
3const yeriaApp = new YeriaApp({ appId: 'my-app' });
4
5const form = yeriaApp
6    .createFormView('user-registration', 'User Registration')
7    .setIntro('Please fill in all required fields')
8    .addTextField('firstName', 'First Name', true, 50)
9    .addTextField('lastName', 'Last Name', true, 50)
10    .addEmailField('email', 'Email Address', true)
11    .addPhoneField('phone', 'Phone Number', false)
12    .submitButton('Create Account', 'POST');
13
14const response = yeriaApp.serve(form);

Formulaire avec liste déroulante

javascript
1const form = yeriaApp
2    .createFormView('survey', 'Customer Survey')
3    .addSelectField('country', 'Country', true, [
4        { label: 'France', value: 'FR' },
5        { label: 'Canada', value: 'CA' },
6        { label: 'Belgium', value: 'BE' }
7    ])
8    .addSelectField('rating', 'Rating', true, [
9        { label: 'Excellent', value: 5 },
10        { label: 'Good', value: 4 },
11        { label: 'Average', value: 3 },
12        { label: 'Poor', value: 2 },
13        { label: 'Very Poor', value: 1 }
14    ])
15    .submitButton('Submit Survey', 'POST');

Formulaire avec envoi de fichier

javascript
1const form = yeriaApp
2    .createFormView('document-upload', 'Upload Document')
3    .addFileField('document', 'Document', true, [
4        'application/pdf',
5        'image/jpeg',
6        'image/png'
7    ])
8    .addPhotoField('photo', 'Profile Photo', false, ['jpeg', 'png'], true)
9    .submitButton('Upload', 'POST');

Formulaire avec enregistrement audio et vidéo

javascript
1const form = yeriaApp
2    .createFormView('incident-report', 'Report an Incident')
3    .addTextField('summary', 'What happened', true, 200)
4    .addAudioField('statement', 'Spoken statement', false, {
5        maxDuration: 120,      // seconds
6        minDuration: 2,        // rejects an accidental tap-and-release
7        source: 'record'       // microphone only — no picking an old file
8    })
9    .addVideoField('evidence', 'Video evidence', false, {
10        maxDuration: 30,       // REQUIRED — this is what bounds the upload size
11        quality: 'low',        // ~480p; ~7 MB per minute
12        source: 'both',        // record now, or pick from the gallery
13        maxSize: 15 * 1024 * 1024
14    })
15    .submitButton('Send report', 'POST');

Comment l'enregistrement parvient au fournisseur

Il n'existe pas d'endpoint d'envoi séparé. Le fichier capturé voyage dans la soumission ordinaire du formulaire : le client émet un multipart/form-data vers la même URL de service validée que celle ayant renvoyé le formulaire, l'enregistrement constituant une partie fichier nommée d'après son fieldId (fieldId_0, fieldId_1, … lorsque multiple est actif), et chacun des autres champs une partie ordinaire.

Le dimensionnement incombe au fournisseur. C'est maxDuration × quality qui borne la charge utile :

qualityRésolution / débit approximatifsPar minute
low480p / ~1 Mbps~7 Mo
medium (par défaut)720p / ~2,5 Mbps~19 Mo
high1080p / ~4 Mbps~30 Mo

L'audio pèse bien moins lourd : à l'encodage retenu par défaut, la voix tourne autour de 0,5 Mo par minute. Ces ordres de grandeur correspondent à la production habituelle des encodeurs mobiles, sans constituer une garantie : réglez la limite de corps de requête du service au-dessus de ce qu'impliquent les contraintes déclarées, avec de la marge. Servez-vous de maxSize pour que le client écarte un fichier trop volumineux avant même d'entamer l'envoi, plutôt que d'échouer en cours de requête.

maxDuration est obligatoire sur video : la construction de la vue lève une erreur en son absence.

Ce que produit réellement une capture

accept déclare ce que le champ accepte. Ce que le moteur de rendu capture est plus restreint, et un fournisseur qui stocke ou rediffuse le fichier doit en tenir compte :

Capturé sous forme de
audioAAC, mono, ~64 kbps, dans un conteneur MP4 (.m4a)
videoH.264 dans un conteneur MP4 (.mp4), à la résolution indiquée par quality
orientationCelle dans laquelle le téléphone était tenu. Portrait comme paysage sont possibles

L'orientation vidéo circule sous forme de métadonnée de rotation MP4, non de pixels déjà pivotés. Les images stockées gardent l'orientation du capteur et le conteneur transporte la rotation à appliquer ; c'est ainsi que tout appareil photo de téléphone écrit une vidéo. Un lecteur qui respecte cette métadonnée affiche la séquence à l'endroit, celui qui l'ignore l'affiche couchée. Si le service réencode, génère une vignette ou diffuse le clip, il lui revient de propager cette rotation : l'oublier explique presque toujours qu'une séquence correcte sur le téléphone se retrouve ensuite de travers.

Un fichier choisi dans la bibliothèque plutôt qu'enregistré peut être tout ce qu'autorise accept : n'importe quelle orientation, n'importe quelle durée.

Revalidez tout côté serveur

Le moteur de rendu fait respecter maxDuration en arrêtant l'enregistreur, et refuse une capture qui contrevient à minDuration ou à maxSize. Rien de tout cela ne constitue une garantie :

  • minDuration n'est vérifié que sur une prise enregistrée. Un fichier choisi dans la bibliothèque n'a pas de durée mesurée : la règle est écartée au lieu d'être appliquée sans élément probant.
  • Chacune de ces contraintes réside dans une charge utile que le client peut très bien ignorer.

Considérez les contraintes déclarées comme le contrat que vous avez demandé, et recontrôlez durée, taille et format à réception.

Formulaire avec champ GPS

javascript
1const form = yeriaApp
2    .createFormView('location-form', 'Record Location')
3    .addGPSField('location', 'Your Location', true, true, {
4        altitude: true,
5        precision: true
6    })
7    .addPlusCodeField('pluscode', 'Plus Code', false, true)
8    .submitButton('Save Location', 'POST');

Formulaire avec champ date

javascript
1const form = yeriaApp
2    .createFormView('appointment', 'Schedule Appointment')
3    .addDateField('appointmentDate', 'Appointment Date', true, '2025-01-01', '2025-12-31')
4    .addTextField('notes', 'Additional Notes', false)
5    .submitButton('Book Appointment', 'POST');

Formulaire pré-rempli

javascript
1const form = yeriaApp
2    .createFormView('edit-profile', 'Edit Profile')
3    .addTextField('name', 'Full Name', true)
4    .addEmailField('email', 'Email', true)
5    .addSelectField('country', 'Country', false, [
6        { label: 'France', value: 'FR' },
7        { label: 'Canada', value: 'CA' }
8    ]);
9
10// Inject pre-filled data
11form.injectData({
12    name: 'John Doe',
13    email: 'john@example.com',
14    country: 'FR'  // Automatically selects the option with value 'FR'
15});
16
17form.submitButton('Update Profile', 'PUT');

Formulaire avec validation

javascript
1const form = yeriaApp
2    .createFormView('registration', 'Register')
3    .addPasswordField('password', 'Password', true, 8)
4    .addTextField('username', 'Username', true)
5    .addField('text', 'username', 'Username', {
6        required: true,
7        minLength: 3,
8        maxLength: 20,
9        pattern: /^[a-zA-Z0-9_]+$/
10    })
11    .submitButton('Register', 'POST');

Formulaire avec séparateurs

javascript
1const form = yeriaApp
2    .createFormView('registration', 'User Registration')
3    .addTextField('firstName', 'First Name', true)
4    .addTextField('lastName', 'Last Name', true)
5    .addSeparator()  // Visual separator between sections
6    .addEmailField('email', 'Email', true)
7    .addPhoneField('phone', 'Phone', false)
8    .addSeparator('billing-separator')  // With explicit ID
9    .addTextField('address', 'Address', true)
10    .addTextField('city', 'City', true)
11    .submitButton('Register', 'POST');

Note : les séparateurs sont purement visuels et ne collectent aucune donnée. Ils sont exclus du contrôle « au moins un champ » : un formulaire doit donc comporter au moins un champ qui ne soit pas un séparateur.

Formulaire dans un processus

javascript
1const form = yeriaApp
2    .createFormView('step-1', 'Personal Information')
3    .belongsToProcess('onboarding', {
4        processName: 'User Onboarding',
5        currentStep: 1,
6        totalSteps: 3,
7        stepName: 'Personal Info',
8        canGoBack: false,
9        canSkip: false
10    })
11    .addTextField('firstName', 'First Name', true)
12    .addTextField('lastName', 'Last Name', true)
13    .submitButton('Next', 'POST');

Exemple JSON complet

json
1{
2  "id": "user-registration",
3  "type": "Form",
4  "content": {
5    "title": "User Registration",
6    "intro": "Please fill in your information to create an account",
7    "submit": {
8      "text": "Register",
9      "method": "POST",
10      "confirmMessage": "Are you sure you want to submit?"
11    },
12    "fields": [
13      {
14        "fieldType": "text",
15        "fieldId": "firstName",
16        "fieldLabel": "First Name",
17        "required": true,
18        "placeholder": "Enter your first name",
19        "maxLength": 50,
20        "helpText": "Your legal first name"
21      },
22      {
23        "fieldType": "text",
24        "fieldId": "lastName",
25        "fieldLabel": "Last Name",
26        "required": true,
27        "placeholder": "Enter your last name",
28        "maxLength": 50
29      },
30      {
31        "fieldType": "separator",
32        "fieldId": "separator-1234567890-1234",
33        "fieldLabel": ""
34      },
35      {
36        "fieldType": "email",
37        "fieldId": "email",
38        "fieldLabel": "Email Address",
39        "required": true,
40        "placeholder": "you@example.com",
41        "pattern": "^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$"
42      },
43      {
44        "fieldType": "phone",
45        "fieldId": "phone",
46        "fieldLabel": "Phone Number",
47        "required": false,
48        "placeholder": "+1234567890"
49      },
50      {
51        "fieldType": "select",
52        "fieldId": "country",
53        "fieldLabel": "Country",
54        "required": true,
55        "options": [
56          {
57            "label": "France",
58            "value": "FR",
59            "selected": false
60          },
61          {
62            "label": "United States",
63            "value": "US",
64            "selected": true
65          },
66          {
67            "label": "Canada",
68            "value": "CA",
69            "selected": false
70          }
71        ]
72      },
73      {
74        "fieldType": "date",
75        "fieldId": "birthDate",
76        "fieldLabel": "Date of Birth",
77        "required": true,
78        "minDate": "1900-01-01",
79        "maxDate": "2010-12-31"
80      },
81      {
82        "fieldType": "number",
83        "fieldId": "age",
84        "fieldLabel": "Age",
85        "required": false,
86        "min": 18,
87        "max": 120,
88        "value": 25
89      },
90      {
91        "fieldType": "audio",
92        "fieldId": "statement",
93        "fieldLabel": "Spoken statement",
94        "required": false,
95        "accept": ["audio/mp4", "audio/mpeg", "audio/wav", "audio/aac"],
96        "maxDuration": 120,
97        "minDuration": 2,
98        "source": "record"
99      },
100      {
101        "fieldType": "video",
102        "fieldId": "evidence",
103        "fieldLabel": "Video evidence",
104        "required": false,
105        "accept": ["video/mp4", "video/quicktime", "video/webm"],
106        "maxDuration": 30,
107        "quality": "low",
108        "source": "both",
109        "maxSize": 15728640
110      }
111    ]
112  },
113  "metadata": {
114    "version": "1.0.0",
115    "createdAt": "2025-01-28T10:00:00.000Z"
116  }
117}