Skip to content
Snippets Groups Projects

Resolve "Fusionner les "select" avec "source" et les "select_custom""

1 file
+ 55
140
Compare changes
  • Side-by-side
  • Inline
+ 55
140
@@ -177,8 +177,6 @@ Dans cet exemple, on définit un seul groupe de champs nommé arbitrairement "fs
Le deuxième et dernier bloc contient les options pour ce module: ici uniquement le lien vers la page de documentation pour ce module (`help`).
Les options peuvent également contenir :
* `defaultNodeType` : le type de section par défaut du module de calcul, pour les modules contenant une section
* `selectIds` : les identifiants des listes déroulantes
* `resultsHelp` : l'aide associée aux résultats (voir ci-dessous)
* `calculateDisabled` : pour masquer le bouton Calculer (ex: module Espèce)
@@ -279,11 +277,13 @@ case CalculatorType.MacroRugoCompound:
Les listes déroulantes sont toujours associées à des **propriétés** du Nub.
En général les valeurs autorisées sont tirées de l'**enum** correspondant, d'après le tableau `Session.enumFromProperty` de JaLHyd. Pour les autres cas, voir les paragraphes "source" et "liste déroulante personnalisée" ci-dessous.
En général les valeurs autorisées sont tirées de l'**enum** correspondant, d'après le tableau `Session.enumFromProperty` de JaLHyd. Pour les autres cas, voir le paragraphe "liste déroulante personnalisée" ci-dessous.
#### configuration
Dans le fichier de configuration du module, ajouter la définition des listes déroulantes dans "fields", notamment la propriété associée et la valeur par défaut. Puis dans le bloc de configuration, déclarer les identifiants des listes dans "selectIds". Exemple dans `trigo/config.json`
Dans le fichier de configuration du module, ajouter la définition des listes déroulantes dans "fields", notamment la propriété associée et la valeur par défaut; cette dernière est optionnelle et en son absence, la 1ère valeur de la liste sera sélectionnée.
Exemple dans `trigo/config.json` (ici le 2ème champ ne spécifie pas de valeur par défaut) :
```json
{
@@ -299,169 +299,108 @@ Dans le fichier de configuration du module, ajouter la définition des listes d
"id": "select_unit",
"type": "select",
"property": "trigoUnit",
"default": "DEG"
},
]
},
{
"type": "options",
"selectIds": [ "select_operation", "select_unit" ],
}
```
**IMPORTANT** : les id doivent être de la forme `select_`_`unmotclesansespacenitirets`_
#### source
Une liste déroulante telle que décrite ci-dessus peut être associée à une **source**, qui détermine quels sont les choix possibles. Ceci est utile lorsque les choix possibles ne proviennent pas d'un `enum`. Pour autant ce n'est pas équivalent à la méthode "liste déroulante personnalisée" décrite au chapitre suivant (ces deux techniques devraient être fusionnées).
Pour ajouter une source, modifier la méthode `loadEntriesFromSource()` de la classe `SelectField`, dans le fichier `src/app/formulaire/elements/select-field.ts`.
Exemple pour la source "remous_target" associée à la propriété "varCalc", dans le module CourbeRemous :
```typescript
switch (source) {
// driven by string[], not enum (easier for variable names)
case "remous_target":
this.addEntry(new SelectEntry(this._entriesBaseId + "none", ""));
for (const at of CourbeRemous.availableTargets) {
const e: SelectEntry = new SelectEntry(this._entriesBaseId + at, at);
this.addEntry(e);
}
break;
```
Ici on ajoute des options de type `SelectEntry` à l'aide de la méthode `addEntry()` : une option vide au début, puis une pour chaque élément d'un tableau.
Puis dans le fichier de configuration du module, déclarer la source :
```json
{
"id": "select_target",
"type": "select",
"property": "varCalc",
"source": "remous_target",
```
**IMPORTANT** : les ids doivent être de la forme `select_`_`unmotclesansespacenitirets`_
#### liste déroulante personnalisée
Il est possible de personnaliser complètement le comportement d'une liste déroulante pour des cas plus complexes, en utilisant l'élément de formulaire `SelectCustom`.
Il est possible de personnaliser complètement le comportement d'une liste déroulante pour des cas plus complexes.
De telles listes doivent être déclarées dans la configuration du module en utilisant le type `select_custom`, un identifiant de `source` (identique à celui vu au chapitre précédent, mais attention : il se comporte différemment) et l'identifiant de la liste doit être mentionné dans l'option `customSelectIds` (et non `selectIds`).
De telles listes doivent être déclarées dans la configuration du module en utilisant uniquement le type `select` et un identifiant de liste. Dans ce cas, on ne fournit pas de champ `property` ni `default`.
Exemple pour la cible du Solveur :
```json
"fields": [
{
"type": "select",
"id": "select_target_nub",
"type": "select_custom",
"source": "solveur_target"
},
]
{
"type": "options",
"customSelectIds": [ "select_target_nub", ],
}
```
Dans le fichier `src/app/formulaire/elements/select-field-custom.ts`, remplir les méthodes `populate()` (définit les choix possibles) et `initSelectedValue()` (affecte la valeur actuellement définie dans le modèle).
Il faut ensuite faire 2 choses :
- créer une classe dérivée de `SelectField` et implémenter les méthodes `populate()` (remplissage de la liste) et `initSelectedValue()` (fixation de la valeur par défaut) :
Exemple pour la cible du Solveur : `populate()`
```typescript
case "solveur_target": // Solveur, module cible (à calculer)
// find all Nubs having at least one link to another Nub's result
candidateNubs =
Session.getInstance().getDownstreamNubs().concat(
Session.getInstance().getUpstreamNubsHavingExtraResults()
).filter(
(element, index, self) => self.findIndex((e) => e.uid === element.uid) === index
);
for (const cn of candidateNubs) {
const nub = fs.getFormulaireFromId(cn.uid);
if (nub) {
const calc = nub.calculatorName;
let label = calc;
// calculated param
if (cn.calculatedParam !== undefined) {
const varName = fs.expandVariableName(cn.calcType, cn.calculatedParam.symbol);
label += ` / ${varName} (${cn.calculatedParam.symbol})`;
}
this.addEntry(new SelectEntry(this._entriesBaseId + cn.uid, cn.uid, decodeHtml(label)));
} else {
// silent fail, this Solveur nub was probably loaded before all the candidate nubs are done loading
}
export class SelectFieldSolverTarget extends SelectField {
protected populate() {
// une série de addEntry()
}
break;
```
Comme au chapitre précédent, il s'agit d'ajouter des options de type `SelectEntry` à l'aide de la méthode `addEntry()`.
protected initSelectedValue() {
// utiliser setValueFromId() avec un id calculé
// ou findAndSetDefaultValue() pour utiliser la 1ère valeur de la liste
}
}
```
Exemple pour la cible du Solveur : `initSelectedValue()`
- modifier la méthode `newSelectField` de la classe `SelectFieldFactory` pour créer une instance de la classe dérivée en utilisant le champ `id` précisé dans le JSON de configuration :
```typescript
case "solveur_target": // Solveur, module cible (à calculer)
const ntc = (nub as Solveur).nubToCalculate;
if (ntc !== undefined) {
this.setValueFromId(this._entriesBaseId + ntc.uid);
public static newSelectField(json: {}, parent: FormulaireNode): SelectField {
switch (json["id"]) {
case "select_target_nub":
return new SelectFieldSolverTarget(parent);
}
}
break;
```
Ici il s'agit de choisir la bonne option du sélecteur, en fonction de la valeur courante de la propriété dans le modèle.
Enfin, ce type de liste déroulante nécessite une classe de formulaire personnalisée, dans laquelle la méthode `update()` doit être enrichie.
Enfin, ce type de liste déroulante peut nécessiter une classe de formulaire personnalisée, dans laquelle la méthode `update()` doit être enrichie.
Exemple dans `src/app/formulaire/definition/form-solveur.ts`, pour la cible du Solveur :
Exemple dans `src/app/formulaire/definition/form-solveur.ts` pour la cible du Solveur :
```typescript
public update(sender: IObservable, data: any) {
if (sender instanceof SelectFieldCustom) {
if (sender.id === "select_target_nub" && data.action === "select") {
// update Solveur property: Nub to calculate
try {
// if searchedParam is set to a value that won't be available anymore
// once nubToCalculate is updated, setPropValue throws an error, but
// nubToCalculate is updated anyway; here, just inhibit the error
this._currentNub.properties.setPropValue("nubToCalculate", data.value.value);
} catch (e) { }
// refresh targetted result selector
const trSel = this.getFormulaireNodeById(this._targettedResultSelectId) as SelectField;
if (trSel) {
(trSel.parent as FieldSet).updateFields();
// trick to re-set observers
this.completeParse(false);
if (sender instanceof SelectField) {
if (sender.id === "select_target_nub" && data.action === "select") {
// update Solveur property: Nub to calculate
try {
// if searchedParam is set to a value that won't be available anymore
// once nubToCalculate is updated, setPropValue throws an error, but
// nubToCalculate is updated anyway; here, just inhibit the error
this._currentNub.properties.setPropValue("nubToCalculate", data.value.value);
} catch (e) { }
// refresh targetted result selector
const trSel = this.getFormulaireNodeById(this._targettedResultSelectId) as SelectField;
if (trSel) {
(trSel.parent as FieldSet).updateFields();
// trick to re-set observers
this.completeParse(false);
}
// refresh parameters selector
this.refreshParameterEntries();
}
// refresh parameters selector
this.refreshParameterEntries();
}
}
}
```
Ici on écoute l'événement généré par l'objet `SelectFieldCustom`, et on agit en conséquence : affecter la propriété concernée, et rafraîchir un champ dépendant.
Ici on écoute l'événement généré par l'objet dérivé de `SelectField`, et on agit en conséquence : affecter la propriété concernée et rafraîchir un champ dépendant.
#### si l'affichage de certains champs dépend du choix dans la liste
Les listes dont l'identifiant est déclaré dans le fichier de configuration du module déclencheront, lorsque leur valeur change, un effacement des résultats du module et une mise à jour de tous les "fieldset" du formulaire.
Cette dernière opération permet de vérifier la visibilité de chaque champ du formulaire, et afficher / masquer ceux dont la visibilité a changé.
Cette dernière opération permet de vérifier la visibilité de chaque champ du formulaire et afficher/masquer ceux dont la visibilité a changé.
Ainsi, pour rendre la visibilité d'un champ dépendante du choix dans la liste, il faut, **dans le code du Nub dans JaLHyd** :
* écouter le changement de propriété (méthode `update()`, action `propertyChange`)
* selon la nouvelle valeur, ajuster la propriété `.visible` des paramètres concernés
* écouter le changement de propriété (méthode `update()`, action `propertyChange`),
* selon la nouvelle valeur, ajuster la propriété `visible` des paramètres concernés.
Il n'y a rien à faire de particulier dans ngHyd.
@@ -469,7 +408,7 @@ Ainsi, pour rendre la visibilité d'un champ dépendante du choix dans la liste,
Il faut utiliser ou étendre `FormulaireSection`.
Dans la configuration du module, ajouter un sélecteur de section, associé à la propriété "nodeType" :
Dans la configuration du module, ajouter un sélecteur de section associé à la propriété "nodeType" (type de section) :
```json
{
@@ -484,17 +423,7 @@ Dans la configuration du module, ajouter un sélecteur de section, associé à l
}
}
```
Puis dans les options, déclarer ce sélecteur et ajouter "defaultNodeType" :
```json
{
"type": "options",
"defaultNodeType": "SectionRectangle",
"selectIds": [ "select_section" ],
}
```
La section par défaut du formulaire sera celle du sélecteur, que celle ci soit ou non configurée explicitement par le champ `default`.
### si le module agrège des modules enfants
@@ -581,14 +510,10 @@ Dans la configuration du module, dans le "fieldset_template", ajouter un sélect
{
"id": "select_structure",
"type": "select",
"property": "structureType",
"source": "device_structure_type"
},
{
"id": "select_loidebit",
"type": "select",
"property": "loiDebit",
"source": "device_loi_debit",
"help": {
"Orifice_OrificeSubmerged": "structures/orifice_noye.html",
"SeuilRectangulaire_WeirVillemonte": "structures/kivi.html",
@@ -602,16 +527,6 @@ Dans la configuration du module, dans le "fieldset_template", ajouter un sélect
}
```
Dans les options, déclarer les sélecteurs :
```json
{
"type": "options",
"selectIds": [ "select_structure", "select_loidebit" ],
}
```
### documentation
Pour chaque langue, ajouter un fichier .md dans les dossiers `docs/*/calculators`, puis placer ce nouveau fichier dans la hiérarchie de la documentation, en ajoutant son chemin dans les fichiers `mkdocs-*.yml`.
Loading