Le contrat properties
Tous les réglages d'un bloc tiennent dans un seul champ objet : properties. Ce qu'on a le droit d'y mettre n'est pas affaire d'habitude — c'est déclaré, type de bloc par type de bloc, dans un fichier de schéma, et une validation refuse le reste.
C'est ce qui rend le panneau de développement possible : il ne connaît aucun bloc en particulier, il lit le schéma et construit les champs qu'il y trouve. Ajouter une propriété à un bloc, c'est donc ajouter une ligne dans son schéma, pas du code dans le panneau.
À quoi ressemble une propriété déclarée
"vl_InputType": {
"section": "blocParameters", // l'accordéon du panneau où elle s'affiche
"control": "select", // COMMENT le panneau la présente
"valueType": "integer", // le TYPE 4D réellement stocké
"label": "BSPK_inputType", // une clé de traduction, jamais du texte en dur
"values": [ ... ], // liste fermée, vérifiée
"visibleWhen": { ... }, // masquée tant qu'une condition n'est pas vraie
"dependents": [ ... ] // champs à redessiner quand celle-ci change
}
control n'est pas valueType
C'est la confusion qui coûte le plus cher, parce qu'elle ne produit aucun message.
control est le widget affiché dans le panneau. valueType est le type 4D de la valeur enregistrée. Les deux diffèrent bien plus souvent qu'on ne l'imagine : vl_InputType se présente comme une liste déroulante et se stocke en entier.
Piège : écrire "0" là où 0 est attendu ne déclenche rien à l'écriture. C'est plus tard que ça se voit — une requête qui ne ramène rien, ou un champ qui se comporte de travers. Aucune erreur n'est levée nulle part dans cette chaîne. En cas de doute, lisez la colonne Type de la fiche de l'objet.
Les types possibles sont : text, integer, boolean, enum, object, collection.
Les drapeaux, et ce qu'ils décident
Un schéma ne décrit pas seulement un type : il porte des drapeaux qui changent le comportement du panneau, du rendu et même du contrôle de sécurité.
| Drapeau | Ce qu'il fait |
|---|---|
| mandatory | La propriété est nécessaire pour que le bloc soit complet. Elle ne bloque pas l'enregistrement — voir plus bas. |
| internal | Pas de champ de saisie : la valeur est écrite par la mécanique du panneau elle-même. |
| supportTranslation | La valeur est stockée par langue, et non comme une valeur simple. |
| allow4D | La valeur peut être une formule 4D au lieu d'un littéral. |
| htmlAttr | La valeur est émise comme attribut HTML sur l'élément rendu. |
| securityConstraint | La valeur est relue par le contrôle de sécurité au retour d'un formulaire. |
| needRefocus | Changer cette valeur oblige à redessiner le champ dans le panneau. |
| multiple | La valeur est une collection, pas une valeur simple. |
| store: "root" | La valeur n'est pas dans le sac : c'est un vrai champ de la table. Un seul cas aujourd'hui, le nom du bloc. |
Ce qui bloque une écriture, et ce qui ne la bloque pas
La validation distingue deux natures de problème, et la distinction est volontaire.
- Les erreurs sont structurelles : propriété inconnue, mauvais type 4D, cible CSS qui n'existe pas. Elles signalent un bug, donc l'écriture est refusée, bruyamment.
- Le « manquant » est une affaire de complétude : une propriété obligatoire laissée vide. L'écriture est acceptée — un travail en cours est légitime. Le bloc porte simplement un indicateur d'incomplétude.
Un nom de propriété inconnu est donc une erreur franche, à trois familles près, parce que des attributs HTML libres sont légitimes : data-*, aria-* et role sont tolérés.
Les champs traduisibles sont des objets
Une propriété marquée supportTranslation ne contient pas une chaîne, mais une valeur par langue :
"vt_FieldLabel": { "fr": "Nom du client", "en": "Customer name" }
La validation vérifie chaque langue séparément. Écrire une chaîne nue là où l'objet de langues est attendu est l'erreur à ne pas commettre. À noter également : le texte riche et les textes longs sont stockés encodés en base64 à l'intérieur de ces valeurs de langue.
Les champs à valeurs multiples
Avec multiple, la valeur est une collection et chaque élément est validé individuellement. La liste fermée n'est imposée que si le champ n'est pas un combo : un combo existe précisément pour accepter des valeurs hors liste — une extension de fichier inhabituelle, par exemple — et les refuser contredirait la définition du champ.
Les champs conditionnels
Deux clés travaillent ensemble, et il faut penser aux deux :
- visibleWhen se pose sur le champ qui se cache : il nomme l'autre propriété et la valeur attendue. L'étoile signifie « dès que l'autre a une valeur, quelle qu'elle soit ».
- dependents se pose sur le champ qui déclenche : il liste les champs à redessiner quand celui-ci change.
Piège : oubliez une entrée dans dependents et le panneau continue d'afficher un champ périmé. Aucune erreur, juste une valeur qui ne correspond plus à ce que l'utilisateur vient de choisir.
La suite
Le style ne passe pas par properties : il a son propre champ, décrit dans Le style : le cube CSS et Tailwind. Et la liste complète des propriétés d'un type de bloc se lit dans sa fiche, à la section Les objets.

