Définition de formulaire Model-Driven
Identifiant du schéma : model-driven-form@2
Version remplacée : model-driven-form@1
(model-driven-form-v1.schema.json) reste
publiée et enregistrée, afin qu’une solution déjà déployée soit toujours validée selon le
schéma pour lequel elle a été conçue. La version 2 ne fait qu’ajouter le champ facultatif
controlProps.lookupView : toute définition v1 est donc aussi une définition v2 valide.
Schéma lisible par machine : model-driven-form.schema.json
La représentation canonique d’un formulaire Model-Driven Apps (section 6.7) — la mise en page utilisée pour consulter et modifier un enregistrement. Validée de manière identique, qu’elle provienne du concepteur, d’un appel d’API, d’un assistant IA ou d’une importation de solution.
Structure
| Champ | Type | Obligatoire | Remarques |
|---|---|---|---|
logicalName | string | oui | Préfixé par l’éditeur, en snake-case minuscule. |
displayName | string | oui | Nom lisible du formulaire. |
entity | string | oui | Le nom logique de la table propriétaire. |
description | string | non | La ligne affichée sous le titre dans la barre de titre du formulaire. |
icon | string | non | L’icône affichée dans la barre de titre du formulaire. |
sections | array | l’un des deux | Une ou plusieurs sections titrées — l’ancienne mise en page à plat (voir ci-dessous). |
tabs | array | l’un des deux | La mise en page plus riche en onglets (voir ci-dessous). |
actions | array | non | Actions de barre de commandes configurables. |
Un formulaire porte soit sections, soit tabs, mais jamais aucun des deux. Les
nouvelles définitions doivent utiliser tabs ; sections est accepté pour que les
formulaires existants continuent de fonctionner, et est traité comme un onglet unique de
lignes à une colonne.
Section (ancienne mise en page sections)
| Champ | Type | Obligatoire | Remarques |
|---|---|---|---|
title | string | oui | Titre de la section. |
columns | integer | oui | Colonnes de mise en page, de 1 à 4. |
fields | array | oui | Placements de champs (voir ci-dessous). |
Onglet (mise en page tabs)
| Champ | Type | Obligatoire | Remarques |
|---|---|---|---|
id | string | non | Id stable attribué par le concepteur. |
title | string | oui | Titre de l’onglet. |
icon | string | non | Icône affichée à côté du titre de l’onglet. |
rows | array | oui | Lignes de mise en page pleine largeur : { id?, columns: 1–4, sections: [...] }, chaque section étant { id?, title, icon?, span?, fields, timeline? }. |
Placement de champ
| Champ | Type | Obligatoire | Remarques |
|---|---|---|---|
field | string | voir remarques | Le nom logique du champ (doit être défini sur la table). Omis uniquement pour un placement Label, qui est un contenu statique et n’est lié à aucun champ. |
label | string | non | Remplacement du libellé ; à défaut, le nom d’affichage du champ est utilisé. |
control | string | non | Le nom logique d’un contrôle personnalisé monté à la place de l’éditeur de champ natif. Le contrôle doit être installé dans l’environnement ; sinon, le champ revient à son éditeur natif. |
controlType | string | non | Le contrôle intégré avec lequel afficher le champ (section 6.13.5). L’un de Text, TextArea, Check, Toggle, Date, DateTime, Time, PersianDate, PersianDateTime, PersianTime, Lookup, Dropdown, Label. À omettre pour laisser le Player choisir la saisie par défaut du type de données du champ. Les variantes Persian… affichent le même sélecteur que leurs équivalents grégoriens, mais s’ouvrent sur le calendrier jalali quelle que soit la langue de l’application ; la valeur stockée reste un instant grégorien. Label est un contenu Markdown statique (controlProps.labelText) sans champ lié. |
required | boolean | non | Indique si le champ est obligatoire pour l’activité sur ce formulaire (section 6.13.5) — l’utilisateur doit le renseigner avant d’enregistrer. Une préoccupation du formulaire, et non une contrainte de base de données (chaque colonne de table accepte null) ; le Player l’applique dans le navigateur, le serveur non. En son absence, c’est le IsRequired propre au champ qui s’applique. |
readOnly | boolean | non | Indique si le champ est affiché en lecture seule sur ce formulaire (affichage, pas modification). Un champ en lecture seule s’affiche en texte simple, sans contrôle de saisie. Absent n’est pas false : absent, le champ est modifiable dans un onglet mais en lecture seule dans header ; un champ d’en-tête devient donc modifiable avec un false explicite. |
controlProps | object | non | Les propriétés du contrôle choisi (voir ci-dessous). |
Priorité des contrôles : un control personnalisé l’emporte sur controlType, qui
l’emporte sur la valeur par défaut du type de données du champ. Omettez les deux et le Player
choisit la valeur par défaut.
« Obligatoire » est une règle de formulaire, pas une contrainte de colonne. required
réside sur le placement : un même champ peut donc être obligatoire sur un formulaire et
facultatif sur un autre, et il n’est vérifié que dans le Player, lorsque l’utilisateur
enregistre. Les colonnes de table acceptent toujours null au niveau de la base de données (à
la manière de Power Apps) : un enregistrement créé par une importation ou un appel d’API
n’est donc jamais rejeté pour un champ obligatoire manquant — la seule valeur qu’exige le
serveur est le champ principal (nom) de la table.
Propriétés du contrôle (controlProps)
| Champ | Type | Lu par | Remarques |
|---|---|---|---|
dateFormat | string | Date, DateTime, Time (et leurs variantes Persian…) | Format d’affichage. Jetons : yyyy/yy, MMMM (nom complet du mois)/MMM (abrégé)/MM/M, dd/d, HH, mm, ss ; tout autre caractère est littéral. En son absence, le format par défaut du calendrier est utilisé — d MMMM yyyy pour le persan (p. ex. 12 اردیبهشت 1405), yyyy/MM/dd pour le grégorien. |
trueLabel | string | Check, Toggle | Le libellé affiché pour l’état « oui ». |
falseLabel | string | Check, Toggle | Le libellé affiché pour l’état « non ». |
rows | integer | TextArea | Nombre de lignes visibles, de 2 à 20. |
lookupEntity | string | Lookup | Nom logique de la table sur laquelle la recherche se résout. |
lookupView | string | Lookup | Nom logique de la vue qui fournit les colonnes affichées dans le panneau de résultats de la recherche. En son absence, la plateforme en résout une — voir le viewType du schéma de vue. |
lookupRowCount | integer | Lookup | Combien de lignes candidates le panneau de la recherche propose à la fois (1–500). En son absence, la valeur par défaut de la plateforme, 10, est utilisée. Le serveur plafonne toute demande — un sélecteur n’est pas un rapport. Ajouté après la v2 par élargissement sur place : toute définition valide sans lui l’est toujours. |
lookupPrimaryField | string | Lookup | Remplacement, propre au contrôle, du champ servant de ligne de nom à chaque candidat. En son absence, le champ principal de la table cible est utilisé ; un nom qui n’est pas un champ de la cible est ignoré. |
lookupSecondaryField | string | Lookup | Remplacement, propre au contrôle, de la seconde ligne sous le nom, qui prend le pas sur le champ désigné par la table cible. En son absence, la désignation de la table est utilisée. |
lookupImageField | string | Lookup | Remplacement, propre au contrôle, du champ File/Image servant d’image à chaque candidat, qui prend le pas sur l’image d’enregistrement de la table cible. En son absence, l’image de la table est utilisée ; un champ qui n’est pas une image est ignoré. |
imageSize | string | Champ Image | La taille d’affichage de l’aperçu d’un champ Image — sm (par défaut, taille d’avatar), md ou lg (une image bien visible). N’a de sens que pour un champ Image. |
labelText | string | Label | Le texte fixe d’un contrôle Label, rendu dans un petit sous-ensemble de Markdown sûr : titres #/##/###, **bold**, *italic*, `code`, listes - , liens [text](url) (http/https/mailto/relatifs uniquement) et {color:name}…{/color} (un nom de couleur autorisé ou #hex). Aucun HTML brut n’est jamais rendu. |
options | array | Dropdown | Options fixes, chacune { value, label }, propres à ce seul champ. Préférez un groupe d’options pour tout ce qui est réutilisé entre champs ou tables : un groupe est un composant partagé et versionné, aux valeurs à l’abri des collisions, alors que ces options sont une liste privée sur un seul formulaire. Un champ dont le type de données est OptionSet ignore ceci et puise dans son groupe. |
Les propriétés qui ne s’appliquent pas au controlType choisi sont ignorées, et non
rejetées — changer le contrôle d’un champ puis revenir en arrière ne fait donc pas perdre
sa configuration.
Chaque field référencé doit être un champ défini sur la table — ce qui est appliqué à
l’enregistrement, en plus de la validation du schéma.
Section chronologie (timeline)
Une section d’un onglet peut être une chronologie au lieu d’un groupe de champs : les
enregistrements d’autres tables dont la recherche pointe vers l’enregistrement ouvert,
fusionnés et du plus récent au plus ancien — les e-mails, appels et notes d’un client dans
une seule colonne. Une section est l’un ou l’autre : lorsque timeline est présent, fields
doit être un tableau vide.
| Champ | Type | Obligatoire | Remarques |
|---|---|---|---|
sources | array (1–10) | oui | Les tables qu’elle lit, une entrée par table (ci-dessous). |
pageSize | integer (5–100) | non | Éléments affichés avant Charger plus ; 20 en son absence. |
Chaque source :
| Champ | Type | Obligatoire | Remarques |
|---|---|---|---|
entity | string | oui | Le nom logique de la table source. |
regarding | string | oui | Une recherche de la table source qui cible la table du formulaire, ou une recherche multitable qui la liste. La chronologie affiche les lignes dont la valeur est l’enregistrement ouvert. |
date | string | oui | Un champ Date et heure de la source (champs système tels que created_at_utc compris). Les éléments sont triés selon ce champ, du plus récent au plus ancien. |
title | string | oui | Le champ qui sert de titre à chaque élément. |
body | string | non | Un champ dont chaque élément affiche les premiers mots. |
type | string | non | Un champ de groupe d’options qui distingue les lignes de la source. Chaque élément prend son icône et sa couleur de son option, le filtre affiche une puce par option, et Nouveau propose une entrée par option, préremplie. |
status | string | non | Un champ affiché à côté de la date de chaque élément. |
À l’enregistrement et à l’importation, chaque nom est vérifié par rapport aux tables de
l’environnement : la table source doit exister, regarding doit pouvoir pointer vers la
table du formulaire, date doit être une date, type doit être un choix, et le reste doit
exister. Une chronologie qui échoue à l’une de ces vérifications est refusée
(modelapps.form.timeline_*) plutôt qu’affichée vide. Supprimer un champ utilisé par une
chronologie le retire de celle-ci : une partie facultative est abandonnée, et une source qui
perd son regarding, sa date ou son title est supprimée.
Exemple
{
"logicalName": "par_invoice_main",
"displayName": "Main",
"entity": "par_invoice",
"description": "The invoice's main form",
"icon": "Receipt",
"tabs": [
{
"id": "tab-general",
"title": "General",
"rows": [
{
"id": "row-1",
"columns": 2,
"sections": [
{
"id": "sec-header",
"title": "Header",
"fields": [
{ "field": "par_name", "required": true },
{ "field": "par_total", "label": "Amount" },
{ "field": "par_number", "readOnly": true },
{
"field": "par_due",
"controlType": "Date",
"controlProps": { "dateFormat": "dd/MM/yyyy" }
},
{
"field": "par_paid",
"controlType": "Toggle",
"controlProps": { "trueLabel": "Paid", "falseLabel": "Unpaid" }
},
{ "field": "par_signature", "control": "par_signature_pad" }
]
}
]
}
]
}
]
}