Suggester
Le composant de Suggester
permet d'avoir un menu déroulant avec une option de recherche.
- Source
- Data
{
"$schema": "../../../lunatic-schema.json",
"suggesters": [
{
"responseNames": [
"VARIABLE_COMMUNE"
],
"name": "L_COMMUNEPASSEE-1-2-0",
"fields": [
{
"name": "label",
"rules": [
"[\\w]+"
],
"language": "French",
"min": 3,
"stemmer": false
},
{
"name": "id",
"rules": [
"[\\w]+"
],
"language": "French",
"min": 3,
"stemmer": false
}
],
"queryParser": {
"type": "tokenized",
"params": {
"language": "French",
"pattern": "[\\w.]+",
"min": 3,
"stemmer": false
}
},
"version": "1"
},
{
"responseNames": [
"VARIABLE_PAYS"
],
"name": "L_PAYS-1-2-0",
"fields": [
{
"name": "label",
"rules": [
"[\\w]+"
],
"language": "French",
"min": 3,
"stemmer": false
}
],
"queryParser": {
"type": "tokenized",
"params": {
"language": "French",
"pattern": "[\\w.]+",
"min": 3,
"stemmer": false
}
},
"version": "1"
},
{
"responseNames": [
"VARIABLE_NATIONALITE"
],
"name": "L_NATIONALITE-1-2-0",
"fields": [
{
"name": "label",
"rules": [
"[\\w]+"
],
"language": "French",
"min": 3,
"stemmer": false
}
],
"queryParser": {
"type": "tokenized",
"params": {
"language": "French",
"pattern": "[\\w.]+",
"min": 3,
"stemmer": false
}
},
"version": "1"
},
{
"responseNames": [
"VARIABLE_PCS"
],
"name": "L_PCS_HOMMES-1-5-0",
"fields": [
{
"name": "label",
"rules": [
"[\\w]+"
],
"language": "French",
"min": 3,
"stemmer": false,
"synonyms": {
"accueil": [
"ACCEUIL"
],
"échafaudage": [
"ECHAFFAUDAGE"
],
"URSSAF": [
"URSAF",
"URSAFF"
],
"ingénierie": [
"INGENIEURIE",
"INGENERIE",
"INGIENERIE"
],
"construction": [
"CONSTRUCTEUR"
],
"distribution": [
"DISTRIBUTEUR"
],
"fabrication": [
"FABRICANT"
],
"abattoir": [
"ABATOIR",
"ABBATOIR",
"ABATOIRE",
"ABATTOIRE"
],
"ascenseur": [
"ASCENCEUR"
],
"ascenseurs": [
"ASCENCEURS"
],
"assenseur": [
"ASSENCEUR"
],
"joaillerie": [
"JOAILLIER"
],
"agroalimentaire": [
"AGGROALIMANTAIRE",
"AGROALIMANTAIRE"
],
"alimentaires": [
"ALIMANTAIRE"
],
"agroalimentaires": [
"AGGROALIMANTAIRES",
"AGROALIMENTAIRES"
]
}
}
],
"queryParser": {
"type": "tokenized",
"params": {
"language": "French",
"pattern": "[\\w.]+",
"min": 3,
"stemmer": false
}
},
"version": "1",
"meloto": true,
"stopWords": [
"a",
"au",
"dans",
"de",
"des",
"du",
"en",
"er",
"la",
"le",
"ou",
"sur",
"d",
"l",
"aux",
"dans",
"un",
"une",
"pour",
"avec",
"chez",
"par",
"les"
]
},
{
"responseNames": [
"VARIABLE_BAILLEURS_SOCIAUX"
],
"name": "bailleurs_sociaux-1-5-0",
"fields": [
{
"name": "label",
"rules": [
"[\\w]+"
],
"language": "French",
"min": 3,
"stemmer": false
}
],
"queryParser": {
"type": "tokenized",
"params": {
"language": "French",
"pattern": "[\\w.]+",
"min": 3,
"stemmer": false
}
},
"version": "1"
}
],
"components": [
{
"componentType": "Suggester",
"response": {
"name": "VARIABLECO"
},
"optionResponses": [
{
"name": "VARIABLECO_NAME",
"attribute": "label"
}
],
"storeName": "L_COMMUNEPASSEE-1-2-0",
"hierarchy": {
"sequence": {
"id": "lt4fhgd6",
"page": "1",
"label": {
"type": "VTL|MD",
"value": "\"I - \" || \"Sequence\""
}
}
},
"conditionFilter": {
"type": "VTL",
"value": "true"
},
"id": "lt4ezymk",
"page": "1",
"label": {
"type": "VTL|MD",
"value": "\"➡ 1. \" || \"Variable Commune\""
},
"optionLabel": {
"type": "VTL",
"value": "\"id || \" - \" || label\""
},
"mandatory": false,
"maxLength": 249
},
{
"componentType": "Suggester",
"response": {
"name": "VARIABLEPA"
},
"storeName": "L_PAYS-1-2-0",
"conditionFilter": {
"type": "VTL",
"value": "true"
},
"id": "lt4fjoev",
"page": "2",
"label": {
"type": "VTL|MD",
"value": "\"➡ 2. \" || \"Variable Pays\""
},
"mandatory": false,
"maxLength": 249
},
{
"componentType": "Suggester",
"storeName": "L_NATIONALITE-1-2-0",
"response": {
"name": "VARIABLENA"
},
"conditionFilter": {
"type": "VTL",
"value": "true"
},
"id": "lt4f6i2y",
"page": "3",
"label": {
"type": "VTL|MD",
"value": "\"➡ 3. \" || \"Variable Nationalité\""
},
"mandatory": false,
"maxLength": 249
},
{
"componentType": "Suggester",
"storeName": "L_PCS_HOMMES-1-5-0",
"response": {
"name": "VARIABLE_P"
},
"conditionFilter": {
"type": "VTL",
"value": "true"
},
"id": "lt4f9q1o",
"page": "4",
"label": {
"type": "VTL|MD",
"value": "\"➡ 4. \" || \"VARIABLE_PCS\""
},
"mandatory": false,
"maxLength": 249
},
{
"componentType": "Suggester",
"storeName": "bailleurs_sociaux-1-5-0",
"response": {
"name": "VARIABLE_B"
},
"conditionFilter": {
"type": "VTL",
"value": "true"
},
"id": "lt4f8uba",
"page": "5",
"label": {
"type": "VTL|MD",
"value": "\"➡ 5. \" || \"VARIABLE_BAILLEURS_SOCIAUX\""
},
"mandatory": false,
"maxLength": 249
}
],
"pagination": "question",
"resizing": {},
"label": {
"type": "VTL|MD",
"value": "Suggester"
},
"lunaticModelVersion": "2.5.0",
"modele": "SUGGESTER",
"enoCoreVersion": "2.7.1",
"generatingDate": "27-02-2024 13:43:43",
"missing": false,
"id": "lt4f6mib",
"maxPage": "5",
"variables": [
{
"variableType": "COLLECTED",
"values": {
"COLLECTED": null,
"EDITED": null,
"INPUTTED": null,
"FORCED": null,
"PREVIOUS": null
},
"name": "COMMENT_QE"
},
{
"variableType": "COLLECTED",
"values": {
"COLLECTED": null,
"EDITED": null,
"INPUTTED": null,
"FORCED": null,
"PREVIOUS": null
},
"name": "VARIABLECO"
},
{
"variableType": "COLLECTED",
"values": {
"COLLECTED": null,
"EDITED": null,
"INPUTTED": null,
"FORCED": null,
"PREVIOUS": null
},
"name": "VARIABLECO_NAME"
},
{
"variableType": "COLLECTED",
"values": {
"COLLECTED": null,
"EDITED": null,
"INPUTTED": null,
"FORCED": null,
"PREVIOUS": null
},
"name": "VARIABLEPA"
},
{
"variableType": "COLLECTED",
"values": {
"COLLECTED": null,
"EDITED": null,
"INPUTTED": null,
"FORCED": null,
"PREVIOUS": null
},
"name": "VARIABLENA"
},
{
"variableType": "COLLECTED",
"values": {
"COLLECTED": null,
"EDITED": null,
"INPUTTED": null,
"FORCED": null,
"PREVIOUS": null
},
"name": "VARIABLE_P"
},
{
"variableType": "COLLECTED",
"values": {
"COLLECTED": null,
"EDITED": null,
"INPUTTED": null,
"FORCED": null,
"PREVIOUS": null
},
"name": "VARIABLE_B"
},
{
"variableType": "CALCULATED",
"expression": {
"type": "VTL",
"value": "true"
},
"name": "FILTER_RESULT_VARIABLECO",
"inFilter": "false"
},
{
"variableType": "CALCULATED",
"expression": {
"type": "VTL",
"value": "true"
},
"name": "FILTER_RESULT_VARIABLEPA",
"inFilter": "false"
},
{
"variableType": "CALCULATED",
"expression": {
"type": "VTL",
"value": "true"
},
"name": "FILTER_RESULT_VARIABLENA",
"inFilter": "false"
},
{
"variableType": "CALCULATED",
"expression": {
"type": "VTL",
"value": "true"
},
"name": "FILTER_RESULT_VARIABLE_P",
"inFilter": "false"
},
{
"variableType": "CALCULATED",
"expression": {
"type": "VTL",
"value": "true"
},
"name": "FILTER_RESULT_VARIABLE_B",
"inFilter": "false"
}
]
}
{}
- Code
- Rendu
import {
type LunaticData,
type LunaticSource,
type LunaticState,
useLunatic,
LunaticComponents,
ModalControls,
} from '@inseefr/lunatic';
import { useState } from 'react';
type Props = {
source: LunaticSource;
data: LunaticData;
options?: {initialPage?: LunaticState['pageTag']},
};
export function FormRendererWithNavigation({ source, data, options }: Props) {
const {
getComponents,
isLastPage,
isFirstPage,
goPreviousPage,
goNextPage,
Provider,
compileControls,
} = useLunatic(source, data, {
initialPage: '1' as LunaticState['pageTag'],
...options
});
// Les contrôles doivent être vérifiés manuellement
const [errors, setErrors] = useState<ReturnType<typeof compileControls>>();
const handleNext = () => {
const currentPageErrors = compileControls();
if (currentPageErrors.currentErrors) {
setErrors(currentPageErrors);
} else {
goNextPage();
}
};
const forceNext = () => {
setErrors(undefined);
goNextPage();
};
const closeModal = () => {
setErrors(undefined);
};
return (
<div>
<Provider>
<LunaticComponents
components={getComponents()}
componentProps={() => ({
errors: errors?.currentErrors,
})}
/>
</Provider>
<div style={{ marginTop: '.7rem', display: 'flex', gap: '.2rem' }}>
<button className="button button--primary" disabled={isFirstPage} onClick={goPreviousPage}>
Précédent
</button>
<button className="button button--primary" disabled={isLastPage} onClick={handleNext}>
Suivant
</button>
</div>
{errors && (
<ModalControls
errors={errors.currentErrors}
goNext={forceNext}
onClose={closeModal}
isCritical={errors.isCritical}
/>
)}
</div>
);
}
Indexation
Dans le fichier source la liste des données à rendre disponible et définit dans la propriété suggesters
. Vous pouvez retrouver les détails sur le format de cette définition en regardant le type SuggesterType dans le code source. Il y a 3 parties importantes :
- fields, permet de définir les champs qui vont être indexé pour la recherche.
- queryParser, permet de définir les règles à appliquer à la chaine de recherche.
- name, nom de la nomenclature, qui sera utilisé dans la propriété
storeName
du composant mais aussi pour la récupération de la nomenclature dansgetReferentiel()
.
Lors de l'initialisation de Lunatic à l'aide du hook useLunatic()
il faut préciser l'option getReferentiel()
qui permet d'indiquer comment charger les données associées au référentiel :
const {} = useLunatic(source, data, {
//...
getReferentiel: async (name) => {
return fetch(`https://insee.fr/referentiel/${name}.json`).then((r) => r.json());
}
})
La nomenclature doit nécessairement contenir un champ id
qui doit être unique pour chaque enregistrement et label
qui sert à l'affichage des options dans le suggester mais peut aussi contenir d'autre champs.
Récupération des données de la nomenclature
Lors de la sélection on peut vouloir récupérer des informations supplémentaires dans la nomenclature (catégorie d'un produit, unité, prix...).
[
{
"id": "brosse",
"label": "Brosse à cheveux",
"price": 20
},
{
"id": "balle",
"label": "Balle rebondissante",
"price": 10
}
]
Dans ce cas, il est possible d'ajoute une option dans le JSON afin d'envoyer la données de certains champs dans des variables spécifique lors d'un choix.
{
"componentType": "Suggester",
"response": {
"name": "PRODUCT"
},
"optionResponses": [
{
"name": "PRODUCT_NAME",
"attribute": "label"
},
{
"name": "PRODUCT_PRICE",
"attribute": "price"
}
]
}
Lors de la sélection d'un élément
PRODUCT
recevra l'ID du produitPRODUCT_NAME
recevra son libelléPRODUCT_PRICE
recevra son prix
Valeur arbitraire
Il est possible de permettre à l'utilisateur de rentrer une valeur arbitraire si aucune option ne correspond à sa recherche.
Dans ce cas-là, il faudra ajouter un champ arbitrary
pour permettre de sauvegarder cette valeur arbitraire.
{
"componentType": "Suggester",
"response": {
"name": "PRODUCT"
},
"arbitrary": {
"response": { "name": "PRODUCT_OTHER" }
}
}
Le champ arbitrary.response
définit la variable qui recevra la valeur arbitraire.