Modèles d'attributs : définitions des champs du formulaire
Structure d'un schéma
{
"type":"object",
"title":"Demande d'adhésion",
"description":"Demande d'adhésion",
"$schema":"http://json-schema.org/draft-07/schema",
"properties":{
attributs...
}
}
Description d'un attribut
Nom du champ
Bloc plié par défaut
La propriété collapsable n'a effet que sur les objets (pas d'effet sur l'objet racine).
S'il y a un cache pour l'état du formulaire, cette directive n'est pas prioritaire sauf s'il y a des champs requis (un cache composant est mis en place pour la qualification d'entrée).
Champ requis
Doit être mentionné dans son parent
Position du champ à l'affichage
Anomalie répertoriée #33043 : comportement inattendu avec les dizaines (ex : 20 est interprété comme 2)
Champ plus petit
Symbole en suffixe du champ
Uniquement pour un champ de type 'number'
Comportement pour la recherche
-
partial: valeur partielle (%search%) -
exact: valeur exacte -
date: pour les dates -
numeric: pour les nombres
Validation de format (front)
Expressions régulières utilisées
À noter que
regexest aussi utilisé quandtypeeststringcoté validation back
Ajout d'un mask pour un champ
-
Pour appliquer un mask de formatage dans un champ, on opte pour la syntaxe indiquée à droite
-
Pour plus de documentation sur les formats et les paramètres du mask : https://github.com/JsDaddy/ngx-mask#examples
"field": {
...
"view": {
...,
"mask": {
"mask": "00 00 00 00 00 00",
"showMaskTyped": true
},
},
...
}
Cacher un champ
Cas d'usage : Un champ
user_managerrécupère un objet d'un référentiel :
displayName: Jean DUPONTidentifier: j.dupontLe champ affiche le
displayNamemais doit stocker son identifiant dans un autre champuser_manager_id(mais qui est technique et pas intéressant pour l'utilisateur).
Afficher une icone de champ auto-évalué
![]()
Cas d'usage : Un champ est rempli coté serveur et n'a pas pour vocation d'interagir avec l'utilisateur
Il est préférable dans ce cas de le mettre en
readOnly.
Valeur par défaut
Permet de proposer une valeur par défaut pour le champ en cours à la création d'un objet.
La valeur dépendra de type de champ (property / field).
Dictionnaire d'attributs
Créer un schéma de dictionnaire d'attributs permet de réutiliser les attributs pour d'autres schémas.
Structure d'un schéma dictionnaire
{
"$schema": "http://json-schema.org/draft-07/schema",
"title": "Dictionnaire d'attributs",
"type": "object",
"$defs": {
"contact": {
...
}
}
}
Appel au dictionnaire
"myBloc": {
"title": "Mon bloc",
"type": "object",
"properties": {
"contact": {
"$ref": "https://<{url.host}>/schemas/<strval(@schema_dictionary->id)>/content#/\$defs/contact"
},
}
}
Conditionner le caractère obligatoire de deux champs : anyOf
Cas d'usage : Un des deux champs
phonedoit être remplis dans un formulaire
"myBloc": {
"title": "Mon bloc",
"type": "object",
"anyOf": [
{
"required": [
"phone"
]
},
{
"required": [
"email"
]
}
],
"properties": {
"phone": {
...
},
"email": {
...
}
}
}
Conditionner l'apparition d'un champ par rapport à un autre
Deux syntaxes sont supportées pour le moment :
Cas simple
Cas d'usage le plus simple avec une seule condition :
- Je sélectionne
vegetarienpour le champregime, le champlegumesapparaît
"properties":{
"regime":{
"title":"Régime",
"type":"string",
"enum":[
"vegetarien",
"carnivore"
],
"view":{
"type":"input-radio"
},
"filterNode":"exact"
}
},
"if": {
"properties": { "regime": { "const": "vegetarien" } }
},
"then": {
"properties": {
"legumes":{
"title":"Légumes",
"type":"array",
"items": {
"type":"string",
"enum":[
"Concombre",
"Carotte"
]
},
"view":{
"type":"input-checkbox"
},
"filterNode":"exact"
}
}
}
Cas complexe
Cas d'usage :
- Je sélectionne
vegetarienpour le champregime, le champlegumesapparaît- Je sélectionne
carnivorepour le champregime, le champviandesapparaît
Ce qui nous donne :
"properties": {
"regime": {
"title":"Régime",
"type":"string",
"enum": [
"vegetarien",
"carnivore"
],
"view": {
"type":"input-radio"
},
"filterNode":"exact"
}
}
"allOf" : [
{
"if": {
"properties": { "regime": { "const": "vegetarien" } }
},
"then": {
"properties": {
"legumes":{
"title":"Légumes",
"type":"array",
"items": {
"type":"string",
"enum":[
"Concombre",
"Carotte"
]
},
"view":{
"type":"input-checkbox"
},
"filterNode":"exact"
}
}
}
},
{
"if": {
"properties": { "regime": { "const": "carnivore" } }
},
"then": {
"properties": {
"viandes":{
"title":"Viandes",
"type":"array",
"items": {
"type":"string",
"enum":[
"Poulet",
"Porc"
]
},
"view":{
"type":"input-checkbox"
},
"filterNode":"exact"
}
}
}
}
]
Référence externe
Si le champ est issu d'un référentiel externe, il faut décrire le service auquel faire appel ainsi que la stratégie d'écriture à mettre en place.
Ci-dessous un exemple d'un code postal issu d'un référentiel externe :
{
"attributesModel_user": {
"content": {
"adresse": {
"codePostal": {
"title": "Code postal",
"type": "string",
"view": {
"type": "input-autocomplete"
}
},
"ville": {
"title": "Ville",
"type": "string",
"view": {
"type": "input"
}
}
}
},
"externals": [
{
"service": "ban",
"bindings": [
{
"attribute": "adresse.codePostal",
"initOnField": true,
"serviceAttribute": "code_postal",
"searchProperty": "code_postal",
"searchType": "start",
"limit": 15,
"sortField": "code_postal",
"sort": "asc",
"isValidationKey": true,
"viewFormat": "{code_postal} - {nom_commune}"
},
{
"attribute": "adresse.ville",
"serviceAttribute": "nom_commune",
"isValidationKey": false
}
]
}
]
}
}
Autres options (à mettre au même niveau que initOnField) :
| Attribut | Description |
|---|---|
loadOnInit |
Charge en amont tout le référentiel dans votre champ |
autoSelectFirstItem |
Sélectionne automatiquement la première valeur du référentiel chargé (loadOnInit requis) |
queryParams |
Permet de passer des filtres dynamiques au service. Exemple : identifier=@me.groups.identifier appliquera un filtre basé sur le contexte utilisateur (@me). |
Référence interne
Si le champ est issu d'un référentiel interne, il faut décrire le service auquel faire appel à la racine du modèle.
ex :
- Le champ
nom_completdéfini dans le blocinformationset le champidentifiant_utilisateurdans le blocautres - Le champ
nick_namedéfini dans le blocinformationset le champ dans les attributsnickNamedans l'attributrootSchemaUseret le blockinformation
{
"attributesModel_user": {
"internals": [
{
"service": "user",
"url": "users",
"bindings": [
{
"attribute": "informations.nom_complet",
"initOnField": true,
"serviceAttribute": "displayName",
"searchProperty": "displayName",
"searchType": "partial",
"limit": 15,
"sortField": "displayName",
"sort": "asc",
"viewFormat": "{displayName}"
},
{
"attribute": "informations.identifiant_utilisateur",
"serviceAttribute": "identifier",
"searchProperty": "identifier",
"isValidationKey": true
},
{
"attribute": "autres.nick_name",
"serviceAttribute": "attributes:rootSchemaUser.information.nickName"
}
]
}
],
"content": {
}
}
}
Autres options (à mettre au même niveau que initOnField) :
| Attribut | Description |
|---|---|
loadOnInit |
Charge en amont tout le référentiel dans votre champ. |
autoSelectFirstItem |
Sélectionne automatiquement la première valeur du référentiel chargé (loadOnInit requis). |
queryParams |
Permet de passer des filtres dynamiques au service. Exemple : identifier=@me.groups.identifier appliquera un filtre basé sur le contexte utilisateur (@me). |
allowEmptyResults |
Définit si une valeur non trouvée dans le référentiel interne est autorisée. Si false, la publication est refusée et une erreur NoRecordFound est levée. Doit être positionné sur le champ ayant isValidationKey: true. |
🆕 À partir de la version 4.0 : l’attribut
allowEmptyResultsa été ajouté.
Exemple avec le filtre @me dans une référence interne
Ici, on utilise le filtre
@medirectement dansqueryParamspour restreindre le référentiel interne aux groupes de l’utilisateur connecté.
{
"attributesModel_user": {
"internals": [
{
"service": "group",
"url": "groups",
"bindings": [
{
"sort": "asc",
"limit": 15,
"attribute": "poste.service",
"sortField": "displayName",
"loadOnInit": true,
"autoSelectFirstItem": true,
"searchType": "partial",
"viewFormat": "{displayName}",
"initOnField": true,
"queryParams": "identifier=@me.groups.identifier",
"searchProperty": "displayName",
"serviceAttribute": "displayName",
"conflictStrategy": "replace"
},
{
"attribute": "poste.service_id",
"searchProperty": "identifier",
"isValidationKey": true,
"serviceAttribute": "identifier",
"allowEmptyResults": false
}
]
}
],
"content": {
}
}
}
Comportement attendu du paramètre allowEmptyResults
allowEmptyResults: false
- Si la valeur saisie par l’utilisateur n’existe pas dans le référentiel lié, la publication est refusée.
- Une erreur de complétude
NoRecordFoundest levée.
allowEmptyResults: true (ou absent)
- La donnée peut être publiée même si elle n’existe pas dans le référentiel.
Pour plus d'informations techniques : InternalService - Principes de fonctionnement
Interfaces disponibles
| Interface | type jsonschema |
filterNode jsonschema |
Gère l'attribut format jsonschema ? |
Gère le référence externe ? | Gère le référence interne ? | Gère l'attribut pattern jsonschema ? |
Gère l'attribut minLength jsonschema ? |
Gère l'attribut maxLength jsonschema ? |
|---|---|---|---|---|---|---|---|---|
| Bloc (section) | object |
|||||||
| Liste déroulante (à choix unique) | enum |
exact |
||||||
| Liste déroulante (à choix multiple) | enum |
exact |
||||||
| Champ autocompletion | string |
exact / partial |
||||||
| Champ texte | string |
exact / partial |
||||||
| Champ textarea | string |
exact / partial |
||||||
| Champ numérique | number / integer |
numeric |
||||||
| Champ date | string |
date |
date) |
|||||
| Champ radio (à choix unique) | enum |
exact |
||||||
| Champ case à cocher (à choix multiple) | array |
exact |
||||||
| Champ toggle | boolean |
exact |
||||||
| Champ image | string |
exact |
Propriétés additionnelles dans les modèles d'attributs
La fonctionnalité additionalProperties permet aux utilisateurs d'enrichir dynamiquement un formulaire avec des champs personnalisés non définis dans le modèle initial.
Pour activer cette fonctionnalité, ajoutez la propriété suivante à la racine de votre modèle d'attributs :

Une fois activée, un bouton + "Ajouter un nouvel attribut" apparaît en bas à droite du formulaire.

- À noter que ce champ ne possède pas de modèle, la définition de son libellé est limité :
Tous caractères spéciaux seront "nettoyés" afin d'éviter des problèmes de clé / valeur liée au JSON - Si la valeur d'un champ personnalisé est effacée, après validation, le champ disparait
Les données saisies dans ces champs supplémentaires sont stockées dans un objet spécial __additionalProperties :
{
"dossier_rh": {
"poste": {
"mutuelle": "Employeur"
},
"collaborateur": {
"nom": "Doe",
"prenom": "John"
},
"__additionalProperties": {
"remarques": "lorem ipsum"
}
}
}
Limitations :
Types de données gérés :
- Texte (avec champ texte)
Exemples jsonschema
Bloc (section)
{
"title":"Adresse",
"description":"Adresse",
"type":"object",
"position": 1,
"properties": { // others attributes... }
}
Liste déroulante (à choix unique)
"sexe": {
"title": "Sexe",
"enum": [ "Féminin", "Masculin" ],
"type": "string",
"view": {
"type": "input-select",
},
"pattern": "^(Féminin|Masculin)$",
"position": 1,
"filterNode": "exact"
}
Liste déroulante (à choix multiple)
Pour un référentiel, l'attribut
loadOnInitest requis, car il faut la liste complète pour que le champ fonctionne correctement.
"sexe": {
"title": "Sexe",
"enum": [ "Féminin", "Masculin" ],
"type": "array",
"view": {
"type": "input-select-multiple",
},
"position": 1,
"filterNode": "exact"
}
Champ auto complété
"color": {
"title": "Couleur",
"enum": [ "Bleu", "Blanc", "Rouge", ],
"type": "string",
"view": {
"type": "input-autocomplete"
},
"position": 1,
"filterNode": "partial"
}
Champ texte
"nom": {
"title": "Nom",
"position": 1,
"type": "string",
"view": {
"type": "input-text"
},
"filterNode": "partial"
}
Champ texte à valeurs multiples
minItems,maxItems,copySeparatoretuniqueItemssont facultatifs.
copySeparatorpermet de définir le caractère utilisé pour l'ajout automatique lors d'une copie (ex : liste d'emails en format scv séparé par un;).Les données sont automatiquement triées par ordre alphabétique.
"recipients": {
"title": "Destinataires",
"position": 1,
"type": "array",
"minItems": 2,
"maxItems": 5,
"uniqueItems": true,
"items": {
"type": "string"
},
"view": {
"type": "input-text-multiple",
"copySeparator": ";"
},
"filterNode": "partial"
}
Champ zone de texte
Peut avoir un attribut
nbLineToShowpour définir le nombre de lignes affichées dans le champ avant scroll. Si pas d'attribut, le champ s'adapte à l'entièreté du texte.Valeurs possibles :
2,3,10,15,20
"story": {
"title": "Récit",
"position": 1,
"type": "string",
"view": {
"type": "input-textarea",
"nbLineToShow": 10
},
"filterNode": "partial"
}
Champ numérique (entiers)
Valeur de
1à10accepté dans cet exempleEnlever
minimumetmaximumsi non limité
"age": {
"title": "Age",
"position": 1,
"type": "integer",
"minimum": 1,
"maximum": 10,
"pattern": '^((-?[1-9])\d*)$',
"view": {
"type": "input-number"
},
"filterNode": "numeric"
}
Champ décimal
Les valeurs sont affichés avec
,mais la valeur stockée est avec.Ex :
20,4=>20.4
"nom": {
"title": "Montant HT",
"position": 1,
"type": "number",
"pattern": "^\d*,?\d*$",
"view": {
"type": "input-number",
"mask": {
"mask":"separator.2",
"suffix": " €"
}
},
"filterNode": "numeric"
}
Champ date
Une date minimum peut être défini ex :
view.minDate: "2025-01-01"(formatYYYY-MMM-DD)Une date maximum peut être défini ex :
view.maxDate: "2025-01-01"(formatYYYY-MMM-DD)La valeur
todaypeut être utilisé pour définir la date du jourATTENTION ! Ceci n'est pas soumis à validation côté API, ceci est purement niveau interface
"birthDate": {
"title": "Date de naissance",
"position": 1,
"type": "string",
"format": "date",
"view": {
"type": "input-date",
"minDate": "1970-01-01",
"maxDate": "today",
"mask": {
"mask":"d0/M0/0000",
"showMaskTyped": true
}
},
"filterNode": "date"
}
Champ radio (à choix unique)
"sexe": {
"title": "Sexe",
"enum": [ "Féminin", "Masculin" ],
"type": "string",
"view": {
"type": "input-radio"
},
"pattern": "^(Féminin|Masculin)$",
"position": 1,
"filterNode": "exact"
}
Champ case à cocher (à choix multiple)
"menu": {
"title": "Choix du menu",
"type": "array",
"items": {
"type": "string",
"enum": [ "Entrée", "Plat", "Dessert", "Boisson" ]
},
"view": {
"type": "input-checkbox"
},
"filterNode": "exact"
}
Champ toggle
"urgence": {
"title": "Urgent",
"type": "boolean",
"default": false,
"view": {
"type": "input-toggle"
},
"filterNode": "exact"
},
Champ image
"avatar": {
"type": "string",
"view": {
"type": "input-img"
},
"title": "Avatar",
"position": 1,
"filterNode":"exact"
}
Champ tableau d'objets
Toutes les fonctionnalités du formulaire sont disponibles pour la definition du schéma dans
ìtemsÀ noter toutefois qu'aucune validation serveur ne s'effectuera si un référentiel avec validation a été paramétré.
"experience": { "type": "array", "view": { "type": "input-array" }, "items": { "type": "object", "properties": { "domaine": { "enum": [ "R&D", "Comptabilité", "Chefferie de projet", "Support", "Infra" ], "type": "string", "view": { "type": "input-select" }, "title": "Domaine d'activité", "position": 1 }, "resume": { "type": "string", "view": { "type": "input-textarea" }, "title": "Résumé", "position": 2 }, "time": { "type": "string", "view": { "type": "input-text" }, "title": "Période", "position": 3 } } }, "title": "Expérience(s) professionelle(s)", "position": 1, "description": "", "uniqueItems": true }
Autres options disponibles :
| Attribut | Type | Description |
|---|---|---|
| minItems | integer | Nombre minimum d'éléments requis dans le tableau |
| maxItems | integer | Nombre maximum d'éléments autorisés dans le tableau |
| uniqueItems | boolean | Si true, tous les éléments du tableau doivent être uniques |
| additionalItems | boolean/object | Schéma additionnel pour les éléments supplémentaires quand items est un tableau de schémas positionnels |
| contains | object | Schéma de validation que le contrat de données doit respecter |
| minContains | integer | Nombre minimum d'éléments qui doivent respecter le schéma contains |
| maxContains | integer | Nombre maximum d'éléments qui peuvent respecter le schéma contains |