diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000000000000000000000000000000000000..560298a3045c52d0ab3ab4eaeeec40ee183c8432
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,24 @@
+{
+    // Use IntelliSense to learn about possible attributes.
+    // Hover to view descriptions of existing attributes.
+    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "type": "chrome",
+            "request": "launch",
+            "name": "Launch Chrome (local webserver)",
+            "url": "http://localhost:4200",
+            "webRoot": "${workspaceFolder}",
+            "runtimeExecutable": "/usr/bin/chromium-browser"
+        },
+        {
+            "name": "Launch Firefox (local webserver)",
+            "type": "firefox",
+            "request": "launch",
+            "reAttach": true,
+            "url": "http://localhost:4200",
+            "webRoot": "${workspaceFolder}"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/jalhyd_branch b/jalhyd_branch
index 1f7391f92b6a3792204e07e99f71f643cc35e7e1..d918178ee85dc2bcfd1fc54e443dc6f25955d946 100644
--- a/jalhyd_branch
+++ b/jalhyd_branch
@@ -1 +1 @@
-master
+45-importation-d-un-parametre-resultat-ou-resultat-complementaire-d-une-autre-calculette
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index f71d031e10b2674c67c337d6f66c4b34bd5de60c..8f495b5da0dbe4234713a14f92f25418f2569097 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -368,7 +368,9 @@ export class AppComponent implements OnInit, OnDestroy, Observer {
    */
   @HostListener('window:beforeunload', ['$event'])
   confirmExit($event) {
-    // affecter une valeur différente de null provoque l'affichage d'un dialogue de confirmation, mais le texte n'est pas affiché
-    $event.returnValue = 'Your data will be lost !';
+    if (environment.production) {
+      // affecter une valeur différente de null provoque l'affichage d'un dialogue de confirmation, mais le texte n'est pas affiché
+      $event.returnValue = 'Your data will be lost !';
+    }
   }
 }
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index e084fe592034151af1d011caaf7dd1d531a7abc8..d5fb120410be0b16bc83e3198dcc029abee4aa4d 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -53,6 +53,7 @@ import { LoadCalculatorComponent } from './components/load-calculator/load-calcu
 import { LoadCalcDialogAnchorDirective } from './components/load-calculator/load-calculator-anchor.directive';
 import { SaveCalculatorComponent } from './components/save-calculator/save-calculator.component';
 import { SaveCalcDialogAnchorDirective } from './components/save-calculator/save-calculator-anchor.directive';
+import { ParamLinkComponent } from './components/param-link/param-link.component';
 
 const appRoutes: Routes = [
   { path: 'list', component: CalculatorListComponent },
@@ -100,7 +101,8 @@ const appRoutes: Routes = [
     FixedResultsComponent, VarResultsComponent,
     HelpComponent,
     LoadCalculatorComponent, LoadCalcDialogAnchorDirective,
-    SaveCalculatorComponent, SaveCalcDialogAnchorDirective
+    SaveCalculatorComponent, SaveCalcDialogAnchorDirective,
+    ParamLinkComponent
   ],
   // entryComponents: [AlertDialog],
   entryComponents: [LoadCalculatorComponent, SaveCalculatorComponent],
diff --git a/src/app/components/field-set/field-set.component.ts b/src/app/components/field-set/field-set.component.ts
index 1c14aafc4185fa19cc05fc904844a2b2a092313d..cbf78ed49f4f3602b7382efd91d71ac4ca0b0ce1 100644
--- a/src/app/components/field-set/field-set.component.ts
+++ b/src/app/components/field-set/field-set.component.ts
@@ -261,6 +261,13 @@ export class FieldSetComponent implements DoCheck {
         this._paramComponents.forEach(fsc => fsc.updateParameterFromUI());
     }
 
+    /**
+     * met à jour les paramètres liés
+     */
+    public updateLinkedParameters() {
+        this._paramComponents.forEach(fsc => fsc.updateLinkedParameter());
+    }
+
     /**
      * clic sur le bouton ajouter
      */
diff --git a/src/app/components/fieldset-container/fieldset-container.component.ts b/src/app/components/fieldset-container/fieldset-container.component.ts
index 240f408b9337099a717173d56df4b9632e1e30ca..947302dee60faf8f7c06174212a4a6641545725d 100644
--- a/src/app/components/fieldset-container/fieldset-container.component.ts
+++ b/src/app/components/fieldset-container/fieldset-container.component.ts
@@ -161,6 +161,13 @@ export class FieldsetContainerComponent implements DoCheck, AfterViewInit {
         this._fieldsetComponents.forEach(fsc => fsc.updateParametersFromUI());
     }
 
+    /**
+     * met à jour les paramètres liés
+     */
+    public updateLinkedParameters() {
+        this._fieldsetComponents.forEach(fsc => fsc.updateLinkedParameters());
+    }
+
     /**
     * réception d'un événement de demande d'ajout d'un FieldSet
     */
diff --git a/src/app/components/generic-calculator/calculator.component.ts b/src/app/components/generic-calculator/calculator.component.ts
index b7b8a07fc9487b2738c8179cf7ee0d1090eaf1bc..2b0a443f97c077b33886bf5f2c44ed3f769c912a 100644
--- a/src/app/components/generic-calculator/calculator.component.ts
+++ b/src/app/components/generic-calculator/calculator.component.ts
@@ -1,4 +1,4 @@
-import { Component, OnInit, DoCheck, OnDestroy, ViewChild, ViewChildren, QueryList } from "@angular/core";
+import { Component, OnInit, DoCheck, OnDestroy, ViewChild, ViewChildren, QueryList, AfterViewChecked } from "@angular/core";
 import { ActivatedRoute } from '@angular/router';
 
 import { Observer } from "jalhyd";
@@ -39,7 +39,7 @@ import { ServiceFactory } from "../../services/service-factory";
     `
     ]
 })
-export class GenericCalculatorComponent extends BaseComponent implements OnInit, DoCheck, OnDestroy, Observer {
+export class GenericCalculatorComponent extends BaseComponent implements OnInit, DoCheck, AfterViewChecked, OnDestroy, Observer {
     /**
      * liste des FieldSetComponent
      */
@@ -89,6 +89,14 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
      */
     private isCalculateDisabled: boolean = true;
 
+    /**
+     * flag (+info) indiquant un événement onRadio à traiter
+     * nécessaire avec l'introduction du mode de valeur LINK des paramètres car quand on modifie le mode à LINK, les possibles
+     * paramètres liables ne sont pas encore connus
+     */
+    private _pendingRadioClick: boolean = false;
+    private _pendingRadioClickInfo: any;
+
     // services
 
     private intlService: InternationalisationService;
@@ -105,7 +113,7 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
     private get formElements(): FormulaireElement[] {
         if (this._formulaire == undefined)
             return [];
-        return this._formulaire.formElements;
+        return this._formulaire.kids as FormulaireElement[];
     }
 
     /**
@@ -193,7 +201,17 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
      * gestion des événements clic sur les radios
      */
     private onRadioClick(info: any) {
-        this._formulaire.onRadioClick(info);
+        this.updateLinkedParameters();
+        this._pendingRadioClick = true;
+        this._pendingRadioClickInfo = info;
+    }
+
+    public ngAfterViewChecked() {
+        if (this._pendingRadioClick) {
+            this._pendingRadioClick = false;
+            this._formulaire.onRadioClick(this._pendingRadioClickInfo);
+            this._pendingRadioClickInfo = undefined;
+        }
     }
 
     private onCloseForm() {
@@ -219,6 +237,14 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
         this._fieldsetContainerComponents.forEach(fscc => fscc.updateParametersFromUI());
     }
 
+    /**
+     * met à jour les paramètres liés
+     */
+    private updateLinkedParameters() {
+        this._fieldsetComponents.forEach(fsc => fsc.updateLinkedParameters());
+        this._fieldsetContainerComponents.forEach(fscc => fscc.updateLinkedParameters());
+    }
+
     private doCompute() {
         this.updateParametersFromUI();
         this._formulaire.doCompute();
diff --git a/src/app/components/ngparam-input/ngparam-input.component.ts b/src/app/components/ngparam-input/ngparam-input.component.ts
index abce24cdfbdc44650ae0a0e7f389f8115023d526..bba17a97f99d03cb6696d95801689d46af2f3386 100644
--- a/src/app/components/ngparam-input/ngparam-input.component.ts
+++ b/src/app/components/ngparam-input/ngparam-input.component.ts
@@ -115,6 +115,20 @@ export class NgParamInputComponent extends GenericInputComponent implements Obse
                     this._tmp = data["value"];
                     this.updateAndValidateUI();
                 }
+                break;
+
+            // changement de valueMode du paramètre ou de valeur à laquelle il est lié
+            case "valueModeChange":
+            case "valueLinkChange":
+                if (this._tmp !== data["value"]) {
+                    this._tmp = data["value"];
+                    this.updateAndValidateUI();
+                }
+                break;
         }
     }
+
+    public ngOnDestroy() {
+        this._paramDef.removeObserver(this);
+    }
 }
diff --git a/src/app/components/param-field-line/param-field-line.component.html b/src/app/components/param-field-line/param-field-line.component.html
index b504852524f154d58adcfce7ab77d1030ab30036..d3ae40fd9df0a92537e7443cd6347772c46d8dcd 100644
--- a/src/app/components/param-field-line/param-field-line.component.html
+++ b/src/app/components/param-field-line/param-field-line.component.html
@@ -27,7 +27,17 @@
             value="cal" (click)="onRadioClick('cal')" [checked]=radioCalCheck [disabled]=isDisabled id="radio_cal">
             {{uitextParamCalculer}}
         </label>
+
+        <!-- radio "lié" -->
+        <label *ngIf="hasRadioLink()" class="{{radioLinkClass}} h-75 px-3 py-3" [(ngModel)]="radioModel" mdbRadio="Right" name="radio_param_{{symbol}}"
+            value="link" (click)="onRadioClick('link')" [checked]=radioLinkCheck [disabled]=isDisabled id="radio_link">
+            {{uitextParamLie}}
+        </label>
     </div>
 </div>
 
-<param-values *ngIf="isRadioVarChecked" [param]="_param" (onValid)=onParamValuesValid($event)></param-values>
\ No newline at end of file
+<!-- composant pour gérer le cas "paramètre à varier" (min-max/liste de valeurs) -->
+<param-values *ngIf="isRadioVarChecked" [param]="_param" (onValid)=onParamValuesValid($event)></param-values>
+
+<!-- composant pour gérer le cas "paramètre lié" -->
+<param-link *ngIf="isRadioLinkChecked" [param]="_param" (onValid)=onParamValuesValid($event)></param-link>
\ No newline at end of file
diff --git a/src/app/components/param-field-line/param-field-line.component.ts b/src/app/components/param-field-line/param-field-line.component.ts
index d43df61f3c4f4a7bd72b86cee476ccf4f992d039..c957e6b1a8a9a766c3ce67f64b083c8b478eccaf 100644
--- a/src/app/components/param-field-line/param-field-line.component.ts
+++ b/src/app/components/param-field-line/param-field-line.component.ts
@@ -4,7 +4,9 @@ import { InternationalisationService } from "../../services/internationalisation
 import { NgParameter, ParamRadioConfig } from "../../formulaire/ngparam";
 import { NgParamInputComponent } from "../ngparam-input/ngparam-input.component";
 import { ServiceFactory } from "../../services/service-factory";
-import { ParamValueMode } from "jalhyd";
+import { ParamValueMode, CalculatorType, ParallelStructure } from "jalhyd";
+import { FormulaireService } from "../../services/formulaire/formulaire.service";
+import { ParamLinkComponent } from "../param-link/param-link.component";
 
 @Component({
     selector: "param-field-line",
@@ -33,6 +35,9 @@ export class ParamFieldLineComponent implements OnChanges {
     @ViewChild(NgParamInputComponent)
     private _ngParamInputComponent: NgParamInputComponent;
 
+    @ViewChild(ParamLinkComponent)
+    private _paramLinkComponent: ParamLinkComponent;
+
     @Output()
     private onValid: EventEmitter<void>;
 
@@ -51,8 +56,11 @@ export class ParamFieldLineComponent implements OnChanges {
 
     private intlService: InternationalisationService;
 
+    private _formService: FormulaireService;
+
     constructor() {
         this.intlService = ServiceFactory.instance.internationalisationService;
+        this._formService = ServiceFactory.instance.formulaireService;
         this.onValid = new EventEmitter();
         this.inputChange = new EventEmitter();
     }
@@ -78,6 +86,10 @@ export class ParamFieldLineComponent implements OnChanges {
         return this.intlService.localizeText("INFO_PARAMFIELD_PARAMCALCULER");
     }
 
+    private get uitextParamLie() {
+        return this.intlService.localizeText("INFO_PARAMFIELD_PARAMLIE");
+    }
+
     /**
      * Parameter symbol (Q, Ks, B, ...) input attribute
      */
@@ -91,7 +103,7 @@ export class ParamFieldLineComponent implements OnChanges {
     private hasRadioFix(): boolean {
         switch (this._param.radioConfig) {
             case ParamRadioConfig.FIX:
-                return false;
+                return this.hasRadioLink();
 
             default:
                 return true;
@@ -125,6 +137,26 @@ export class ParamFieldLineComponent implements OnChanges {
         }
     }
 
+    /**
+    * calcule la présence du radio "paramètre lié" (importé d'une autre calculette)
+    */
+    private hasRadioLink(): boolean {
+        if (this._formService.formulaires.length > 0) {
+            // au moins 2 calculettes ouvertes
+            if (this._formService.formulaires.length > 1)
+                return this._formService.filterLinkableValues(this._formService.getLinkableValues(this._param)).length > 0;
+
+            // ou une seule calculette "ouvrages parallèles"
+            if (this._formService.formulaires[0].calculatorType == CalculatorType.ParallelStructure) {
+                const ps: ParallelStructure = this._formService.formulaires[0].currentSessionNub.nub as ParallelStructure;
+                if (ps.structures.length > 1)
+                    return this._formService.filterLinkableValues(this._formService.getLinkableValues(this._param)).length > 0;
+            }
+
+        }
+        return false;
+    }
+
     /**
     * calcule l'état du radio "paramètre fixé"
     */
@@ -148,6 +180,15 @@ export class ParamFieldLineComponent implements OnChanges {
         return undefined;
     }
 
+    /**
+    * calcule l'état du radio "paramètre lié"
+    */
+    private get radioLinkCheck(): string {
+        if (this._param.radioState == ParamRadioConfig.LINK)
+            return "checked";
+        return undefined;
+    }
+
     /**
      * retourne l'état du radio "paramètre fixé" sous forme booléenne
      */
@@ -162,6 +203,13 @@ export class ParamFieldLineComponent implements OnChanges {
         return this._param.radioState == ParamRadioConfig.VAR;
     }
 
+    /**
+     * retourne l'état du radio "paramètre lié" sous forme booléenne
+     */
+    private get isRadioLinkChecked(): boolean {
+        return this._param.radioState == ParamRadioConfig.LINK;
+    }
+
     /*
      * gestion des événements clic sur les radios :
      * envoi d'un message au composant parent
@@ -173,9 +221,12 @@ export class ParamFieldLineComponent implements OnChanges {
 
     private onRadioClick(option: string) {
         const oldValue = this._param.valueMode;
+
         switch (option) {
             case "fix":
+                const oldValueMode = this._param.valueMode;
                 this._param.valueMode = ParamValueMode.SINGLE;
+                this._param.setValue(this, this._param.paramDefinition.paramValues.singleValue);
                 break;
 
             case "var":
@@ -185,6 +236,10 @@ export class ParamFieldLineComponent implements OnChanges {
             case "cal":
                 this._param.valueMode = ParamValueMode.CALCUL;
                 break;
+
+            case "link":
+                this._param.valueMode = ParamValueMode.LINK;
+                break;
         }
 
         this.onRadio.emit({
@@ -240,6 +295,15 @@ export class ParamFieldLineComponent implements OnChanges {
         return "";
     }
 
+    /**
+     * classe du radio "lié"
+     */
+    private get radioLinkClass(): string {
+        if (this.on)
+            return this.radioLinkCheck == undefined ? this.offClass : this.onClass;
+        return "";
+    }
+
     /**
      * validité des saisies du composant
      */
@@ -302,4 +366,12 @@ export class ParamFieldLineComponent implements OnChanges {
     public updateParameterFromUI() {
         this._ngParamInputComponent.updateModelFromUI();
     }
+
+    /**
+     * met à jour les paramètres liés
+     */
+    public updateLinkedParameter() {
+        if (this._paramLinkComponent !== undefined)
+            this._paramLinkComponent.updateParamList();
+    }
 }
diff --git a/src/app/components/param-link/param-link.component.html b/src/app/components/param-link/param-link.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..1fc1166766aeb759588caf42bd7188988076d4c3
--- /dev/null
+++ b/src/app/components/param-link/param-link.component.html
@@ -0,0 +1,13 @@
+<div class="row">
+    <div class="btn-group col-6 col-sm-3" dropdown (click)="onSelectLinkableParam($event)">
+        <button dropdownToggle class="btn btn-primary dropdown-toggle waves-light my-1" type="button" mdbRippleRadius>
+            {{currentLinkedParamLabel}}
+        </button>
+        <div class="dropdown-menu">
+            <a class="dropdown-item" *ngFor="let e of _linkableParams" [value]=e>{{selectItemLabel(e)}}</a>
+        </div>
+    </div>
+    <div class="col-6 text-danger">
+        {{_message}}
+    </div>
+</div>
\ No newline at end of file
diff --git a/src/app/components/param-link/param-link.component.ts b/src/app/components/param-link/param-link.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d7b5bc013a4ff9ac4a0a134bb0160055693a1d48
--- /dev/null
+++ b/src/app/components/param-link/param-link.component.ts
@@ -0,0 +1,200 @@
+import { Component, Input, Output, EventEmitter, OnChanges, OnDestroy } from "@angular/core";
+
+import { NgParameter } from "../../formulaire/ngparam";
+import { ServiceFactory } from "../../services/service-factory";
+import { ParamValueMode, Observer, ParamDefinition } from "jalhyd";
+import { FormulaireService } from "../../services/formulaire/formulaire.service";
+import { FormulaireDefinition } from "../../formulaire/definition/form-definition";
+
+@Component({
+    selector: "param-link",
+    templateUrl: "./param-link.component.html"
+})
+export class ParamLinkComponent implements OnChanges, Observer, OnDestroy {
+    // paramètre géré (qui sera lié à une valeur, cad qui importe cette valeur)
+    @Input("param")
+    private _param: NgParameter;
+
+    @Output()
+    private onValid: EventEmitter<boolean>;
+
+    /**
+     * indice actuel du paramètre sélectionné dans la lsite
+     */
+    private _currentIndex = -1;
+
+    /**
+     * message affiché à côté du select des paramètres
+     */
+    private _message: string;
+
+    /**
+     * liste des paramètres liables sous la forme
+     * {"name":<étiquette>, "value":<valeur liable>, "nub":<Nub d'origine du paramètre>, "formTitle":<nom de la calculette liée au nub>}
+     * 
+     * l'étiquette "name" est de la forme <n|N1>[.[N2]]
+     * n : indice de de l'ouvrage dans le cas des ouvrages parallèles
+     * N1 : un nom de paramètre/résultat (dans le cas d'un résultat, il est suivi d'un point)
+     * N2 : nom de résultat complémentaire (optionnel)
+     * ex : 
+     *   Q, Z1 (paramètres)
+     *   J. (résultat)
+     *   .Yf (résultat complémentaire du résultat courant)
+     *   Q.Yf (résultat complémentaire du résultat nommé "Q")
+     */
+    private _linkableParams: any[];
+
+    private _formService: FormulaireService;
+
+    constructor() {
+        this.onValid = new EventEmitter();
+        this._formService = ServiceFactory.instance.formulaireService;
+        this._formService.addObserver(this);
+    }
+
+    /**
+     * envoi d'un événement de validité
+     */
+    private emitValidity() {
+        // this.onValid.emit(this._validList);
+    }
+
+    /**
+     * réception d'un événement du menu des paramètres liables
+     */
+    private onSelectLinkableParam(event: any) {
+        const next = event.target.value;
+
+        let i = 0;
+        for (const e of this._linkableParams)
+            if (this._linkableParams[i].value.uid == next.value.uid) {
+                this.linkTo(i);
+                break;
+            }
+            else
+                i++;
+    }
+
+    /**
+     * valeur courante affichée dans le select des paramètres liables
+     */
+    private get currentLinkedParamLabel(): string {
+        if (this._linkableParams !== undefined) {
+            if (this._currentIndex === -1 || this._currentIndex >= this._linkableParams.length)
+                return undefined;
+
+            return this.selectItemLabel(this._linkableParams[this._currentIndex]);
+        }
+
+        return undefined;
+    }
+
+    //     // le paramètre est il déjà lié à une valeur ? si oui laquelle ?
+    //     if(this._currentIndex === -1 && this._param.valueMode == ParamValueMode.LINK && this._linkableParams !== undefined) {
+    //     let i = 0;
+    //     for (const e of this._linkableParams)
+    //         if (e.param.uid === this._param.paramDefinition.re) {
+    //             this._currentIndex = i;
+    //             break;
+    //         }
+    //         else
+    //             i++;
+    // }
+
+    /**
+     * attribut "label" d'une entrée du select des paramètres
+     */
+    private selectItemLabel(i: any) {
+        const s = i.name; // nom associé au paramètre/à la valeur
+        const c = i.formTitle; // nom de la calculette
+
+        const re5 = /(\d+)\.(.+)\.$/;  // forme <nombre>.xxx. (résultat d'ouvrage)
+        const match5 = re5.exec(s);
+        if (match5 !== null) {
+            const n = +match5[1] + 1
+            return `${match5[2]} (résultat de ${c}, ouvrage n°${n})`;
+        }
+
+        const re1 = /([^\.]+)\.$/;  // forme xxx. (résultat)
+        const match1 = re1.exec(s);
+        if (match1 !== null)
+            return `${match1[1]} (résultat de ${c})`;
+
+        const re4 = /(\d+)\.(.+)/;  // forme <nombre>.xxx (ouvrage)
+        const match4 = re4.exec(s);
+        if (match4 !== null) {
+            const n = +match4[1] + 1
+            return `${match4[2]} (${c}, ouvrage n°${n})`;
+        }
+
+        const re2 = /([^\.]+)\.(.+)/;  // forme xxx.yyy (résultat complémentaire)
+        const match2 = re2.exec(s);
+        if (match2 !== null)
+            return `${match2[2]} (${c}, résultat complémentaire de ${match2[1]})`;
+
+        const re3 = /^\.(.+)/;  // forme .xxx (résultat complémentaire)
+        const match3 = re3.exec(s);
+        if (match3 !== null)
+            return `${match3[1]} (${c}, résultat complémentaire)`;
+
+        return `${s} (${c})`; // forme simple (paramètre)
+    }
+
+    /**
+     * lie le paramètre géré à un des paramètres liables de la liste
+     * @param index indice dans la liste
+     */
+    private linkTo(index: number) {
+        if (this._currentIndex !== index) {
+            this._currentIndex = index;
+            const lp = this._linkableParams[index];
+
+            this._param.linkToParameter(lp.nub, lp.name);
+        }
+    }
+
+    public updateParamList() {
+        // liste des paramètres liables
+
+        if (this._param.valueMode === ParamValueMode.LINK)
+            this._linkableParams = this._formService.filterLinkableValues(this._formService.getLinkableValues(this._param));
+        else
+            this._linkableParams = [];
+
+        // initialisation de l'indice courant
+
+        if (this._linkableParams.length > 0) {
+            if (this._currentIndex === -1)
+                this.linkTo(0);
+            this._message = undefined;
+        }
+        else {
+            this._currentIndex = -1;
+            this._message = "Aucun paramètre compatible trouvé";
+        }
+    }
+
+    public ngOnChanges() {
+        if (this._param !== undefined)
+            this._param.removeObserver(this);
+        this._param.addObserver(this);
+
+        this.updateParamList();
+    }
+
+    public ngOnDestroy() {
+        this._param.removeObserver(this);
+    }
+
+    // interface Observer 
+
+    public update(sender: any, data: any) {
+        if (sender instanceof FormulaireService) {
+            switch (data["action"]) {
+                case "createForm":
+                    this.updateParamList();
+                    break;
+            }
+        }
+    }
+}
diff --git a/src/app/components/param-values/param-values.component.html b/src/app/components/param-values/param-values.component.html
index d231d60368df700bfdf4e0473e6b24cc22c8f818..0335d8bddfbd2a6ee34e288de944edd1d8b307c5 100644
--- a/src/app/components/param-values/param-values.component.html
+++ b/src/app/components/param-values/param-values.component.html
@@ -8,13 +8,13 @@
         </div>
     </div>
 
-    <div *ngIf="!isList" class="col-12 col-sm-3">
+    <div *ngIf="isMinMax" class="col-12 col-sm-3">
         <ngparam-min [title]="uitextValeurMini" (onChange)="onMinChanged($event)"></ngparam-min>
     </div>
-    <div *ngIf="!isList" class="col-12 col-sm-3">
+    <div *ngIf="isMinMax" class="col-12 col-sm-3">
         <ngparam-max [title]="uitextValeurMaxi" (onChange)="onMaxChanged($event)"></ngparam-max>
     </div>
-    <div *ngIf="!isList" class="col-12 col-sm-3">
+    <div *ngIf="isMinMax" class="col-12 col-sm-3">
         <ngparam-step [title]="uitextPasVariation" [param]=_param (onChange)="onStepChanged($event)"></ngparam-step>
     </div>
 
diff --git a/src/app/components/param-values/param-values.component.ts b/src/app/components/param-values/param-values.component.ts
index 91200b4644d6621f42c394c380a805950d2a171c..36761b432690baecbecafc7447b664ab7e593491 100644
--- a/src/app/components/param-values/param-values.component.ts
+++ b/src/app/components/param-values/param-values.component.ts
@@ -281,6 +281,13 @@ export class ParamValuesComponent extends BaseComponent implements AfterViewChec
         return this._param.valueMode == ParamValueMode.LISTE;
     }
 
+    /**
+     * true si mode "lié"
+     */
+    private get isLink(): boolean {
+        return this._param.valueMode == ParamValueMode.LINK;
+    }
+
     /**
      * true si mode "min/max/pas"
      */
diff --git a/src/app/components/remous-results/remous-results.component.ts b/src/app/components/remous-results/remous-results.component.ts
index 07bbca115f0eb052f226c231adda47df5729d129..a986c11d1f9f34b312d801ad697fffbefa089b49 100644
--- a/src/app/components/remous-results/remous-results.component.ts
+++ b/src/app/components/remous-results/remous-results.component.ts
@@ -1,12 +1,11 @@
 import { Component, ViewChild } from "@angular/core";
 
-import { ArrayReverseIterator, ResultElement, ParamValueIterator } from "jalhyd";
+import { ArrayReverseIterator, ResultElement, NumberIterator } from "jalhyd";
 
 import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
 import { LogComponent } from "../../components/log/log.component";
 import { RemousResults } from "../../results/remous-results";
 import { CalculatorResults } from "../../results/calculator-results";
-import { VarResults } from "../../results/var-results";
 import { VarResultsComponent } from "../fixedvar-results/var-results.component";
 
 @Component({
@@ -17,12 +16,12 @@ import { VarResultsComponent } from "../fixedvar-results/var-results.component";
         text-align: right;
         padding-top:10px;
         padding-bottom:10px;
-        padding-right:10px;   
+        padding-right:10px;
     }
     .result_value {
         text-align: center;
-        padding-left:30px;   
-        padding-right:30px;   
+        padding-left:30px;
+        padding-right:30px;
     }
     .result_id_0 {
         background-color: #f0f0f0;
@@ -58,7 +57,7 @@ export class RemousResultsComponent {
     /**
      * true si les résultats doivent être mis à jour
      */
-    private _doUpdate: boolean = false;
+    private _doUpdate = false;
 
     /**
      * composant des résultats variables
@@ -76,54 +75,56 @@ export class RemousResultsComponent {
     }
 
     private get uitextLigneFluviale() {
-        return this.intlService.localizeText("INFO_REMOUSRESULTS_LIGNEFLUVIALE")
+        return this.intlService.getExtraResLabel("FLU");
     }
 
     private get uitextLigneTorrentielle() {
-        return this.intlService.localizeText("INFO_REMOUSRESULTS_LIGNETORRENTIELLE")
+        return this.intlService.getExtraResLabel("TOR");
     }
 
     private get uitextAbscisse() {
-        return this.intlService.localizeText("INFO_REMOUSRESULTS_ABSCISSE")
+        return this.intlService.localizeText("INFO_REMOUSRESULTS_ABSCISSE");
     }
 
     private get uitextTirant() {
-        return this.intlService.localizeText("INFO_REMOUSRESULTS_TIRANT")
+        return this.intlService.localizeText("INFO_REMOUSRESULTS_TIRANT");
     }
 
     private get uitextFond() {
-        return this.intlService.localizeText("INFO_REMOUSRESULTS_FOND")
+        return this.intlService.localizeText("INFO_REMOUSRESULTS_FOND");
     }
 
     private get uitextBerge() {
-        return this.intlService.localizeText("INFO_REMOUSRESULTS_BERGE")
+        return this.intlService.localizeText("INFO_REMOUSRESULTS_BERGE");
     }
 
     private get uitextTirantNormal() {
-        return this.intlService.localizeText("INFO_REMOUSRESULTS_TIRANTNORMAL")
+        return this.intlService.localizeText("INFO_REMOUSRESULTS_TIRANTNORMAL");
     }
 
     private get uitextTirantCritique() {
-        return this.intlService.localizeText("INFO_REMOUSRESULTS_TIRANTCRITIQUE")
+        return this.intlService.localizeText("INFO_REMOUSRESULTS_TIRANTCRITIQUE");
     }
 
     private get extraGraph(): boolean {
-        return this._remousResults == undefined ? false : this._remousResults.extraGraph;
+        return this._remousResults === undefined ? false : this._remousResults.extraGraph;
     }
 
     private get extraParamLabel(): string {
-        return this._remousResults == undefined ? undefined : this._remousResults.extraParamLabel;
+        return this._remousResults === undefined ? undefined :
+            this.intlService.getExtraResLabel(this._remousResults.extraParamSymbol);
     }
 
     public set results(rs: CalculatorResults[]) {
         this._remousResults = undefined;
-        if (rs != undefined)
+        if (rs !== undefined) {
             for (const r of rs) {
                 if (r instanceof RemousResults) {
                     this._remousResults = r;
                     break;
                 }
             }
+        }
         this.updateView();
     }
 
@@ -132,26 +133,30 @@ export class RemousResultsComponent {
         this.graph1_options = {};
         this.graph2_data = {};
         this.graph2_options = {};
-        if (this.varResultsComponent != undefined)
+        if (this.varResultsComponent !== undefined) {
             this.varResultsComponent.results = undefined;
-        if (this.logComponent != undefined)
+        }
+        if (this.logComponent !== undefined) {
             this.logComponent.log = undefined;
+        }
         this._tableHeaders = [];
 
-        if (this._remousResults != undefined)
+        if (this._remousResults !== undefined) {
             this._doUpdate = this._remousResults.hasResults;
+        }
     }
 
-    /** 
+    /**
      * appelé pour gérer les changements non détectés par Angular
      */
     public ngDoCheck() {
-        if (this._doUpdate)
+        if (this._doUpdate) {
             this._doUpdate = !this.updateResults();
+        }
     }
 
     private updateResults() {
-        if (this.logComponent != undefined && this._remousResults != undefined) {
+        if (this.logComponent !== undefined && this._remousResults !== undefined) {
             this.logComponent.log = this._remousResults.log;
             this.generateGraph();
             return true;
@@ -159,62 +164,66 @@ export class RemousResultsComponent {
         return false;
     }
 
-    private get abscisseIterator(): ParamValueIterator {
+    private get abscisseIterator(): NumberIterator {
         return this._remousResults.varResults.variatedParameter.paramDefinition.paramValues.getValuesIterator();
     }
 
     private connectRessaut(lineFlu: LineData, lineTor: LineData) {
-        if (lineFlu != undefined && lineTor != undefined) {
-            let tX = lineFlu.tx.slice(0); // copie
+        if (lineFlu !== undefined && lineTor !== undefined) {
+            const tX = lineFlu.tx.slice(0); // copie
 
             tX.sort((a, b) => {
-                if (a > b)
+                if (a > b) {
                     return 1;
-                if (a < b)
+                }
+                if (a < b) {
                     return -1;
+                }
                 return 0;
             });
 
             let minXflu; // abscisse de début de la courbe fluviale
             let itX = this.abscisseIterator;
-            for (let re of this._remousResults.result.resultElements) {
-                if (!itX.hasNext)
-                    throw new Error("RemousResultsComponent.connectRessaut() : erreur interne (itérateur sur x)")
+            for (const re of this._remousResults.result.resultElements) {
+                if (!itX.hasNext) {
+                    throw new Error("RemousResultsComponent.connectRessaut() : erreur interne (itérateur sur x)");
+                }
                 const x = itX.next().value;
-                if (re.getExtraResult("flu") != undefined) {
+                if (re.getExtraResult("flu") !== undefined) {
                     minXflu = x;
                     break;
                 }
             }
 
-            if (minXflu != undefined && minXflu != tX[0]) {
+            if (minXflu !== undefined && minXflu !== tX[0]) {
                 // la courbe fluviale ne démarre pas au début, on ajoute un point de raccord avec la ligne torrentielle
 
-                let i = tX.indexOf(minXflu);
-                let xflu = tX[i - 1];
-                let yflu = lineTor.getYat(xflu);
+                const i = tX.indexOf(minXflu);
+                const xflu = tX[i - 1];
+                const yflu = lineTor.getYat(xflu);
                 lineFlu.setPoint(xflu, yflu);
             }
 
             let maxXtor; // abscisse de fin de la courbe torrentielle
             const itRE = new ArrayReverseIterator<ResultElement>(this._remousResults.result.resultElements);
             itX = this.abscisseIterator;
-            for (let r of itRE) {
-                if (!itX.hasNext)
-                    throw new Error("RemousResultsComponent.connectRessaut() : erreur interne (itérateur sur x)")
+            for (const r of itRE) {
+                if (!itX.hasNext) {
+                    throw new Error("RemousResultsComponent.connectRessaut() : erreur interne (itérateur sur x)");
+                }
                 const x = itX.next();
-                if (r.getExtraResult("tor") != undefined) {
+                if (r.getExtraResult("tor") !== undefined) {
                     maxXtor = x;
                     break;
                 }
             }
 
-            if (maxXtor != undefined && maxXtor != tX[tX.length - 1]) {
+            if (maxXtor !== undefined && maxXtor !== tX[tX.length - 1]) {
                 // la courbe torrentielle ne finit pas à la fin des abscisses, on ajoute un point de raccord avec la ligne fluviale
 
-                let i = tX.indexOf(maxXtor);
-                let xflu = tX[i + 1];
-                let yflu = lineFlu.getYat(xflu);
+                const i = tX.indexOf(maxXtor);
+                const xflu = tX[i + 1];
+                const yflu = lineFlu.getYat(xflu);
                 lineTor.setPoint(xflu, yflu);
             }
         }
@@ -225,86 +234,109 @@ export class RemousResultsComponent {
         // le dernier dataset de la liste datasets est dessiné en 1er
 
         this._remousResults.update();
-        if (this.varResultsComponent)
+        if (this.varResultsComponent) {
             this.varResultsComponent.results = this._remousResults.varResults;
+        }
 
         const penteFond: number = this._remousResults.penteFond;
 
         // abscisses
 
         let labs: number[] = [];
+        let xmax: number;
         if (this._remousResults.result.ok) {
-            var xmax = -1e8;
-            const itX = this.abscisseIterator;
-            while (itX.hasNext) {
-                const x = itX.next().value;
+            xmax = -1e8;
+            const itX2 = this.abscisseIterator;
+            while (itX2.hasNext) {
+                const x = itX2.next().value;
                 labs.push(x);
                 xmax = Math.max(x, xmax);
             }
-        }
-        else {
+        } else {
             labs = [0, 1];
             xmax = 1;
         }
 
         // init graphiques
 
-        let gr1 = new GraphData(labs, penteFond, xmax);
-        if (this._remousResults.extraGraph)
-            var gr2 = new GraphData(labs, 0, xmax);
+        const gr1 = new GraphData(labs, penteFond, xmax);
+        let gr2: GraphData;
+        if (this._remousResults.extraGraph) {
+            gr2 = new GraphData(labs, 0, xmax);
+        }
 
         // ligne de fond
         gr1.drawLine(0, 0, 3, "#753F00", this.uitextFond, "#753F00");
 
         // ligne de berge
-        if (this._remousResults.hautBerge)
+        if (this._remousResults.hautBerge) {
             gr1.drawLine(this._remousResults.hautBerge, this._remousResults.hautBerge, 4, "#C58F50", this.uitextBerge);
+        }
 
         // hauteur normale
-        if (this._remousResults.hautNormale != undefined && this._remousResults.hautNormale.ok)
-            gr1.drawLine(this._remousResults.hautNormale.vCalc, this._remousResults.hautNormale.vCalc, 5, "#A4C537", this.uitextTirantNormal);
+        if (this._remousResults.hautNormale !== undefined && this._remousResults.hautNormale.ok) {
+            gr1.drawLine(this._remousResults.hautNormale.vCalc, this._remousResults.hautNormale.vCalc,
+                5, "#A4C537", this.uitextTirantNormal
+            );
+        }
 
         // hauteur critique
-        if (this._remousResults.hautCritique != undefined && this._remousResults.hautCritique.ok)
-            gr1.drawLine(this._remousResults.hautCritique.vCalc, this._remousResults.hautCritique.vCalc, 6, "#FF0000", this.uitextTirantCritique);
+        if (this._remousResults.hautCritique !== undefined && this._remousResults.hautCritique.ok) {
+            gr1.drawLine(this._remousResults.hautCritique.vCalc, this._remousResults.hautCritique.vCalc,
+                6, "#FF0000", this.uitextTirantCritique
+            );
+        }
 
         // lignes d'eau torrentielle et fluviale
 
-        if (this._remousResults.hasFluData)
-            var lineFlu = gr1.newLine(0);
-        if (this._remousResults.hasTorData)
-            var lineTor = gr1.newLine(1);
+        let lineFlu: LineData;
+        if (this._remousResults.hasFluData) {
+            lineFlu = gr1.newLine(0);
+        }
+        let lineTor: LineData;
+        if (this._remousResults.hasTorData) {
+            lineTor = gr1.newLine(1);
+        }
+        let lineExtra: LineData;
         if (this._remousResults.hasExtra) {
-            if (this._remousResults.extraGraph)
-                var lineExtra = gr2.newLine(2);
-            else
+            if (this._remousResults.extraGraph) {
+                lineExtra = gr2.newLine(2);
+            } else {
                 lineExtra = gr1.newLine(2);
+            }
         }
 
         const itX = this.abscisseIterator;
-        for (let re of this._remousResults.result.resultElements) {
+        for (const re of this._remousResults.result.resultElements) {
             if (!itX.hasNext)
-                throw new Error("RemousResultsComponent.generateGraph() : erreur interne (itérateur sur x)")
+                throw new Error("RemousResultsComponent.generateGraph() : erreur interne (itérateur sur x)");
 
             const x = itX.next().value;
-            const yExtra = re.getExtraResult("tRes");
-            if (yExtra != undefined)
+            const yExtra = re.getExtraResult(this._remousResults.extraParamSymbol);
+            if (yExtra !== undefined)
                 lineExtra.mapPoint(x, yExtra);
 
             const yFlu = re.getExtraResult("flu");
-            if (yFlu != undefined)
+            if (yFlu !== undefined)
                 lineFlu.mapPoint(x, yFlu);
 
             const yTor = re.getExtraResult("tor");
-            if (yTor != undefined)
+            if (yTor !== undefined)
                 lineTor.mapPoint(x, yTor);
         }
 
         if (this._remousResults.hasExtra) {
-            if (this._remousResults.extraGraph)
-                lineExtra.data = { label: this._remousResults.extraParamLabel, tension: 0, spanGaps: true, borderColor: "#0093BD", pointRadius: 4 };
-            else
-                lineExtra.data = { label: this._remousResults.extraParamLabel, tension: 0, fill: false, spanGaps: true, borderColor: "#C17AF0", pointRadius: 4 };
+            if (this._remousResults.extraGraph) {
+                lineExtra.data = {
+                    label: this.extraParamLabel,
+                    tension: 0, spanGaps: true, borderColor: "#0093BD", pointRadius: 4
+                };
+            } else {
+                lineExtra.data = {
+                    label: this.extraParamLabel,
+                    tension: 0, fill: false, spanGaps: true, borderColor: "#C17AF0", pointRadius: 4
+                };
+            }
         }
 
         // raccordement ligne fluviale -> torrentielle pour dessiner le ressaut
@@ -313,9 +345,9 @@ export class RemousResultsComponent {
 
         // ajout des données au graphique
 
-        if (lineTor != undefined)
+        if (lineTor !== undefined)
             lineTor.data = { label: this.uitextLigneTorrentielle, tension: 0, borderColor: "#77A3CD", pointBackgroundColor: "#77A3CD", pointRadius: 4, backgroundColor: "#D1D0D4" };
-        if (lineFlu != undefined)
+        if (lineFlu !== undefined)
             lineFlu.data = { label: this.uitextLigneFluviale, tension: 0, borderColor: "#0093BD", pointBackgroundColor: "#0093BD", pointRadius: 4, backgroundColor: "#D1D0D4" };
 
         this.graph1_data = gr1.data;
@@ -368,7 +400,7 @@ export class RemousResultsComponent {
     }
 
     private get hasResults(): boolean {
-        return this._remousResults != undefined && this._remousResults.hasResults;
+        return this._remousResults !== undefined && this._remousResults.hasResults;
     }
 
     private get hasData(): boolean {
@@ -414,12 +446,12 @@ class LineData {
     }
 
     public getYat(x: number) {
-        let i = this._tx.indexOf(x);
+        const i = this._tx.indexOf(x);
         return this._ty[i];
     }
 
     public setPoint(x: number, y: number) {
-        let i = this._tx.indexOf(x);
+        const i = this._tx.indexOf(x);
         this._ty[i] = y;
     }
 
@@ -436,7 +468,7 @@ class LineData {
     }
 
     public hasYs(): boolean {
-        for (let y of this._ty)
+        for (const y of this._ty)
             if (y != null)
                 return true;
         return false;
@@ -481,7 +513,7 @@ class GraphData {
      * @param z profondeur de la lign
      */
     public newLine(z: number): LineData {
-        let res = new LineData(this);
+        const res = new LineData(this);
         res.z = z;
         this._lines.push(res);
         return res;
@@ -515,19 +547,19 @@ class GraphData {
      * @param fillColor couleur de remplissage sous la ligne
      */
     public drawLine(y0: number, ymax: number, prof: number, color: string, lbl: string, fillColor: string = undefined) {
-        let l = this.newLine(prof);
+        const l = this.newLine(prof);
         l.mapPoint(0, y0);
         l.mapPoint(this._longBief, ymax);
 
         // l.data = { label: lbl, data: l, fill: fillColor != undefined, tension: 0, borderColor: color, backgroundColor: fillColor, pointRadius: 0 };
         l.data = {
-            label: lbl, fill: fillColor != undefined, tension: 0, spanGaps: true,
+            label: lbl, fill: fillColor !== undefined, tension: 0, spanGaps: true,
             borderColor: color, backgroundColor: fillColor, pointRadius: 0
         };
     }
 
     public get data() {
-        let ds = [];
+        const ds = [];
         this._lines.sort((a, b) => {
             if (a.z > b.z)
                 return -1;
@@ -536,7 +568,7 @@ class GraphData {
             return 0;
         });
 
-        for (let l of this._lines)
+        for (const l of this._lines)
             ds.push(l.data);
 
         return {
diff --git a/src/app/components/result-element/vertical-result-element.component.ts b/src/app/components/result-element/vertical-result-element.component.ts
index 53fff99656de4a8e04de9b02562dac6bf7d28a7a..ae8f7c09c0ef8511914c5550006cd8aba0e224f0 100644
--- a/src/app/components/result-element/vertical-result-element.component.ts
+++ b/src/app/components/result-element/vertical-result-element.component.ts
@@ -54,7 +54,7 @@ export class VerticalResultElementComponent extends ResultElementBaseComponent {
                 const lblClass = (i % 2) == 0 ? "label1" : "label2";
                 const valueClass = (i % 2) == 0 ? "value1" : "value2";
                 this.vcRef.createEmbeddedView(this.trTemplate, {
-                    extraRes: { "label": this.intlService.translateLabel(k), "value": this.intlService.formatResult(k, er) },
+                    extraRes: { "label": this.intlService.getExtraResLabel(k), "value": this.intlService.formatResult(k, er) },
                     classes: { "label_class": lblClass, "value_class": valueClass }
                 });
                 i++;
diff --git a/src/app/components/section-results/section-results.component.ts b/src/app/components/section-results/section-results.component.ts
index 0656b917ad31ae06cce6956c66e4734f40a2d57f..f296b115b73fdb4931c4eb04cf92b2de36632b14 100644
--- a/src/app/components/section-results/section-results.component.ts
+++ b/src/app/components/section-results/section-results.component.ts
@@ -16,12 +16,12 @@ import { InternationalisationService } from '../../services/internationalisation
         text-align: right;
         padding-top:10px;
         padding-bottom:10px;
-        padding-right:10px;   
+        padding-right:10px;
     }
     .result_value {
         text-align: center;
-        padding-left:30px;   
-        padding-right:30px;   
+        padding-left:30px;
+        padding-right:30px;
     }
     .result_id_0 {
         background-color: #f0f0f0;
@@ -82,7 +82,7 @@ export class SectionResultsComponent implements DoCheck {
             this._doUpdate = this._results.hasResults;
     }
 
-    /** 
+    /**
      * appelé pour gérer les changements non détectés par Angular
      */
     public ngDoCheck() {
@@ -105,8 +105,7 @@ export class SectionResultsComponent implements DoCheck {
 
             // traduction des symboles des variables calculées
             for (const k in this._results.result.extraResults) {
-                const k2 = "INFO_GRANDEUR_" + k.toUpperCase();
-                const lbl = this.intlService.localizeText(k2);
+                const lbl = k.toUpperCase();
                 const er = this._results.result.getExtraResult(k);
                 this._resultElement.addExtraResult(lbl, er);
 
diff --git a/src/app/formulaire/check-field.ts b/src/app/formulaire/check-field.ts
index 95f1afcdb45ee0a00720fe2e42c60ac6a5339a28..f508e699a1714362f7f21720c1431a943adcf734 100644
--- a/src/app/formulaire/check-field.ts
+++ b/src/app/formulaire/check-field.ts
@@ -1,7 +1,4 @@
 import { Field } from "./field";
-import { Dependency } from "./dependency/dependency";
-import { DependencyConditionType } from "./dependency/dependency-condition";
-import { FormulaireDefinition } from "./definition/form-definition";
 import { FormulaireNode } from "./formulaire-node";
 
 export class CheckField extends Field {
diff --git a/src/app/formulaire/definition/concrete/form-courbe-remous.ts b/src/app/formulaire/definition/concrete/form-courbe-remous.ts
index c4653bbace083e3b72966a065e931accb31f74b2..f67df0d0bad7a6af51e35c75cd330a1dddfeb63d 100644
--- a/src/app/formulaire/definition/concrete/form-courbe-remous.ts
+++ b/src/app/formulaire/definition/concrete/form-courbe-remous.ts
@@ -6,7 +6,6 @@ import { FormComputeCourbeRemous } from "../form-compute-courbe-remous";
 import { FormulaireDefinition } from "../form-definition";
 import { CalculatorResults } from "../../../results/calculator-results";
 import { FieldSet } from "../../fieldset";
-import { SelectField } from "../../select-field";
 
 export class FormulaireCourbeRemous extends FormulaireDefinition {
     private _formSection: FormDefSection;
diff --git a/src/app/formulaire/definition/concrete/form-regime-uniforme.ts b/src/app/formulaire/definition/concrete/form-regime-uniforme.ts
index db3fd0e698109c5bdfe1fb55a1ef8e936ad96818..611391ed1fd4c7e89fcebafbd018d20fa3053c12 100644
--- a/src/app/formulaire/definition/concrete/form-regime-uniforme.ts
+++ b/src/app/formulaire/definition/concrete/form-regime-uniforme.ts
@@ -76,7 +76,6 @@ export class FormulaireRegimeUniforme extends FormulaireDefinition implements Ob
 
     public reset() {
         super.reset();
-        this._formParamCalc.parseOptions(this.jsonConfig);
     }
 
     // interface Observer
diff --git a/src/app/formulaire/definition/form-compute-courbe-remous.ts b/src/app/formulaire/definition/form-compute-courbe-remous.ts
index 664a17cf419b94ecf9266cbc6cec2abbe4eeb76e..69f3a832a3ce2f1c39faeb372d16b568dbe0e307 100644
--- a/src/app/formulaire/definition/form-compute-courbe-remous.ts
+++ b/src/app/formulaire/definition/form-compute-courbe-remous.ts
@@ -22,31 +22,30 @@ export class FormComputeCourbeRemous extends FormCompute {
         const prmCR: CourbeRemousParams = cr.parameters as CourbeRemousParams;
         const sect: acSection = prmCR.Sn;
 
-        let Yn: Result = sect.Calc("Yn"); // hauteur normale
-        let Yc: Result = sect.Calc("Yc"); // hauteur critique
+        const Yn: Result = sect.Calc("Yn"); // hauteur normale
+        const Yc: Result = sect.Calc("Yc"); // hauteur critique
 
         this.remousResults.parameters = prmCR;
 
         // méthode de résolution
 
-        let msf: SelectField = <SelectField>this._formBase.getFormulaireNodeById("select_resolution");
-        let methRes: MethodeResolution = msf.getValue().value;
+        const msf: SelectField = <SelectField>this._formBase.getFormulaireNodeById("select_resolution");
+        const methRes: MethodeResolution = msf.getValue().value;
 
         // variable supplémentaire à calculer
 
-        const extraSymbol: string = this._formBase.getSelectedValue("select_target");
+        this.remousResults.extraParamSymbol = this._formBase.getSelectedValue("select_target");
 
         // calcul
 
-        this.remousResults.result = cr.calculRemous(extraSymbol);
+        this.remousResults.result = cr.calculRemous(this.remousResults.extraParamSymbol);
 
         // données du graphe
 
         this.remousResults.hauteurNormale = Yn.resultElement;
         this.remousResults.hauteurCritique = Yc.resultElement;
-        if (extraSymbol) {
-            this.remousResults.extraParamLabel = this._formBase.getSelectedLabel("select_target");
-            this.remousResults.extraGraph = ["Hs", "Hsc", "Yf", "Yt", "Yco"].indexOf(extraSymbol) == -1;
+        if (this.remousResults.extraParamSymbol) {
+            this.remousResults.extraGraph = ["Hs", "Hsc", "Yf", "Yt", "Yco"].indexOf(this.remousResults.extraParamSymbol) === -1;
         }
         else
             this.remousResults.extraGraph = false;
diff --git a/src/app/formulaire/definition/form-compute-fixedvar.ts b/src/app/formulaire/definition/form-compute-fixedvar.ts
index 1955e9350f40905ef743ed1e3a3be5eac17474e3..9285a6704e1367ad8523b7738120f08ae4924fee 100644
--- a/src/app/formulaire/definition/form-compute-fixedvar.ts
+++ b/src/app/formulaire/definition/form-compute-fixedvar.ts
@@ -15,7 +15,16 @@ export class FormComputeFixedVar extends FormCompute {
     }
 
     private getVariatedParameter(): NgParameter {
-        return this._formBase.getDisplayedParamFromState(ParamRadioConfig.VAR);
+        let res = this._formBase.getDisplayedParamFromState(ParamRadioConfig.VAR);
+        if (res !== undefined)
+            return res;
+
+        const pms = this._formBase.getDisplayedParamListFromState(ParamRadioConfig.LINK);
+        for (const p of pms)
+            if (p.paramDefinition.hasMultipleValues)
+                return p;
+
+        return undefined;
     }
 
     private getComputedParameter(): NgParameter {
diff --git a/src/app/formulaire/definition/form-def-fixedvar.ts b/src/app/formulaire/definition/form-def-fixedvar.ts
index 3c00d56577f7229e2720125787701b420d62e6fc..9016a8a3081104112b212f1231f9c5af7f892f47 100644
--- a/src/app/formulaire/definition/form-def-fixedvar.ts
+++ b/src/app/formulaire/definition/form-def-fixedvar.ts
@@ -19,33 +19,103 @@ export class FormDefFixedVar {
     /**
      * remet les radios de tous les paramètres à FIX sauf "me" et ceux (celui) à l'état "except"
      */
-    protected resetOtherRadio(me: NgParameter, except: ParamRadioConfig) {
+    protected resetOtherRadio(me: NgParameter, except: ParamRadioConfig = undefined) {
         for (const p of this._formBase.allFormElements) {
             if (p instanceof NgParameter)
-                if (p != me && p.radioState != except && p.radioConfig != ParamRadioConfig.FIX)
+                if (p != me && p.radioState != except && p.radioState != ParamRadioConfig.LINK && p.radioConfig != ParamRadioConfig.FIX)
                     p.valueMode = ParamValueMode.SINGLE;
         }
     }
 
+    /**
+     * gère un changement de mode pour un paramètre
+     * règles :
+     *   - 1 seul paramètre CAL à la fois
+     *   - 1 seul paramètre multivalué (VAR) à la fois (directement ou par liaison)
+     *   - plusieurs paramètres FIX à la fois possible
+     *   - plusieurs paramètres LINK à la fois possible si les 2 1ères règles sont respectées
+     * 
+     * analyse :
+     * ancien état    nouvel état    action(s)
+     * FIX            VAR            action1
+     * FIX            CAL            action2
+     * FIX            LINK           si paramètre lié FIX : aucune
+     *                               si paramètre lié VAR : action1
+     *                               si paramètre lié CAL : si valeur unique : aucune
+     *                                                      si valeur multiple : action1
+     *                               si paramètre lié LINK : recommencer ce cas avec le paramètre lié
+     * 
+     * VAR            FIX            aucune
+     * VAR            CAL            action2
+     * VAR            LINK           si paramètre lié FIX : aucune
+     *                               si paramètre lié VAR : aucune
+     *                               si paramètre lié CAL : si valeur unique : aucune
+     *                                                      si valeur multiple : aucune
+     *                               si paramètre lié LINK : recommencer ce cas avec le paramètre lié
+     * 
+     * CAL            FIX            action5
+     * CAL            VAR            action3 + action5
+     * CAL            LINK           si paramètre lié FIX : aucune
+     *                               si paramètre lié VAR : action3 + action4|action5
+     *                               si paramètre lié CAL : action3 + action4|action5
+     *                               si paramètre lié LINK : recommencer ce cas avec le paramètre lié
+     *
+     * action1 : reset (à FIX) de tous les autres paramètres que celui modifié sauf celui à CAL
+     * action2 : reset (à FIX) de tous les autres paramètres que celui modifié sauf celui/ceux à VAR
+     * action3 : reset (à FIX) de tous les autres paramètres que celui modifié
+     * action4 : mettre le paramètre désigné par la conf comme "par défault" à CAL
+     * action5 : mettre le 1er paramètre de la calculette à CAL
+     */
     protected processRadioStateChange(sourceParam: NgParameter, oldState: ParamValueMode) {
         switch (oldState) {
-            case ParamValueMode.SINGLE:
-                switch (sourceParam.valueMode) { // nouvel état
-                    case ParamValueMode.MINMAX:
+            case ParamValueMode.SINGLE:  // ancien état
+                switch (sourceParam.valueMode) {
+                    case ParamValueMode.MINMAX:  // nouvel état
                     case ParamValueMode.LISTE:
                         this.resetOtherRadio(sourceParam, ParamRadioConfig.CAL);
                         break;
 
-                    case ParamValueMode.CALCUL:
+                    case ParamValueMode.CALCUL:  // nouvel état
                         this.resetOtherRadio(sourceParam, ParamRadioConfig.VAR);
                         break;
+
+                    case ParamValueMode.LINK:  // nouvel état
+                        if (sourceParam.paramDefinition.hasMultipleValues)
+                            this.resetOtherRadio(sourceParam, ParamRadioConfig.CAL);
+                        else {
+                            const refParamValues = sourceParam.paramDefinition.referencedParamValues;
+                            if (refParamValues !== undefined) // cad si on référence un paramètre et non un Result par ex
+                                if (refParamValues.valueMode == ParamValueMode.LINK)
+                                    throw new Error(`références de paramètre en chaîne non pris en charge`); // cas à traiter
+                        }
+                        break;
                 }
                 break;
 
-            case ParamValueMode.LISTE:
+            case ParamValueMode.LISTE:  // ancien état
             case ParamValueMode.MINMAX:
-                switch (sourceParam.valueMode) { // nouvel état
-                    case ParamValueMode.CALCUL:
+                switch (sourceParam.valueMode) {
+                    case ParamValueMode.CALCUL:  // nouvel état
+                        this.resetOtherRadio(sourceParam, ParamRadioConfig.VAR);
+                        break;
+
+                    case ParamValueMode.LINK:  // nouvel état
+                        // mode du paramètre référencé
+                        const refParamValues = sourceParam.paramDefinition.referencedParamValues;
+                        if (refParamValues.valueMode === ParamValueMode.LINK)
+                            throw new Error(`références de paramètre en chaîne non pris en charge`)
+                        break;
+                }
+                break;
+
+            case ParamValueMode.LINK:  // ancien état
+                switch (sourceParam.valueMode) {
+                    case ParamValueMode.MINMAX:  // nouvel état
+                    case ParamValueMode.LISTE:
+                        this.resetOtherRadio(sourceParam, ParamRadioConfig.CAL);
+                        break;
+
+                    case ParamValueMode.CALCUL:  // nouvel état
                         this.resetOtherRadio(sourceParam, ParamRadioConfig.VAR);
                         break;
                 }
@@ -81,6 +151,13 @@ export class FormDefFixedVar {
         }
     }
 
+    private logParams() {
+        console.log("----");
+        for (const fe of this._formBase.allFormElements)
+            if (fe instanceof NgParameter)
+                console.log(`${fe.paramDefinition.symbol} : ${ParamValueMode[fe.paramDefinition.valueMode]}`);
+    }
+
     /**
      * gestion des événements clic sur les radios
      */
@@ -88,6 +165,8 @@ export class FormDefFixedVar {
         const param: NgParameter = info.param; // paramètre source de l'événement radio
         const old: ParamValueMode = info.oldValueMode; // ancien état (radio)
 
+        //this.logParams();
         this.resetRadiosAndResults(param, old);
+        //this.logParams();
     }
 }
diff --git a/src/app/formulaire/definition/form-def-paramcalc.ts b/src/app/formulaire/definition/form-def-paramcalc.ts
index 67bcd3939c70cfb13c729c0afacc1a8e26628337..de7d95b77ac5efcba949cb26594d2e4f8b36cd52 100644
--- a/src/app/formulaire/definition/form-def-paramcalc.ts
+++ b/src/app/formulaire/definition/form-def-paramcalc.ts
@@ -38,28 +38,56 @@ export class FormDefParamToCalculate extends FormDefFixedVar {
     }
 
     /**
-     * met le paramètre par défaut à CAL
+     * met le paramètre par défaut à CAL sauf si c'est "except"
+     * @param except paramètre à ne pas remettre à CAL
      */
-    private setDefault(): NgParameter {
+    private setDefault(except: NgParameter = undefined): NgParameter {
         const defaultParamCal: NgParameter = this._formBase.getParamFromSymbol(this._defaultCalculatedParam);
-        defaultParamCal.valueMode = ParamValueMode.CALCUL;
+        if (except === undefined || defaultParamCal.uid !== except.uid)
+            defaultParamCal.valueMode = ParamValueMode.CALCUL;
         return defaultParamCal;
     }
 
+    /**
+     * @see FormDefFixedVar.processRadioStateChange pour l'analyse
+     */
     protected processRadioStateChange(sourceParam: NgParameter, oldState: ParamValueMode) {
         super.processRadioStateChange(sourceParam, oldState);
 
         switch (oldState) {
-            case ParamValueMode.CALCUL:
-                switch (sourceParam.valueMode) {  // nouvel état
-                    case ParamValueMode.SINGLE:
-                        this.setDefault();
+            case ParamValueMode.CALCUL:  // ancien état
+                switch (sourceParam.valueMode) {
+                    case ParamValueMode.SINGLE:  // nouvel état
+                        this.setDefault(sourceParam);
                         break;
 
-                    case ParamValueMode.MINMAX:
+                    case ParamValueMode.MINMAX:  // nouvel état
                     case ParamValueMode.LISTE:
-                        super.resetOtherRadio(sourceParam, ParamRadioConfig.CAL);
-                        this.setDefault();
+                        super.resetOtherRadio(sourceParam);
+                        this.setDefault(sourceParam);
+                        break;
+
+                    case ParamValueMode.LINK:  // nouvel état
+                        if (sourceParam.paramDefinition.hasMultipleValues) {
+                            super.resetOtherRadio(sourceParam);
+                            this.setDefault();
+                        }
+                        else {
+                            // mode du paramètre référencé
+                            const refParamValues = sourceParam.paramDefinition.referencedParamValues;
+                            if (refParamValues !== undefined)
+                                switch (refParamValues.valueMode) {
+                                    case ParamValueMode.MINMAX:
+                                    case ParamValueMode.LISTE:
+                                    case ParamValueMode.CALCUL:
+                                        super.resetOtherRadio(sourceParam);
+                                        this.setDefault();
+                                        break;
+
+                                    case ParamValueMode.LINK:
+                                        throw new Error(`références de paramètre en chaîne non pris en charge`); // cas à traiter
+                                }
+                        }
                         break;
                 }
         }
diff --git a/src/app/formulaire/definition/form-definition.ts b/src/app/formulaire/definition/form-definition.ts
index c9d0506967eeb3b97296704e826bfa8125904d6b..85656392cacfc0b289727e5f80d452017d648624 100644
--- a/src/app/formulaire/definition/form-definition.ts
+++ b/src/app/formulaire/definition/form-definition.ts
@@ -14,7 +14,6 @@ import { DeepFieldsetIterator } from "../form-iterator/deep-fieldset-iterator";
 import { DeepFormulaireElementIterator } from "../form-iterator/deep-element-iterator";
 import { TopFormulaireElementIterator } from "../form-iterator/top-element-iterator";
 import { CalculatorResults } from "../../results/calculator-results";
-import { FieldsetTemplate } from "../fieldset-template";
 
 /**
  * classe de base pour tous les formulaires
@@ -183,7 +182,7 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs
     private parse_template_container(json: {}, templates: any[]) {
         const fsc: FieldsetContainer = new FieldsetContainer(this);
         fsc.parseConfig(json, templates);
-        this.formElements.push(fsc);
+        this.kids.push(fsc);
     }
 
     public parseDependencies(json: {}) {
@@ -383,13 +382,9 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs
         return select.getValue().label;
     }
 
-    public get formElements(): FormulaireElement[] {
-        return this.kids as FormulaireElement[];
-    }
-
     public applyDependencies() {
         for (const fe of this.topFormElements)
-            fe.applyDependencies(this);
+            fe.applyDependencies();
     }
 
     public abstract resetResults();
diff --git a/src/app/formulaire/definition/form-result-fixedvar.ts b/src/app/formulaire/definition/form-result-fixedvar.ts
index 0dacc750a71041b2f37d20c05d72a2d652fa99da..695bc2059247e2fe753f826ab9bb94ce8cc56f53 100644
--- a/src/app/formulaire/definition/form-result-fixedvar.ts
+++ b/src/app/formulaire/definition/form-result-fixedvar.ts
@@ -1,4 +1,4 @@
-import { ResultElement, cLog } from "jalhyd";
+import { ResultElement, cLog, ParamValueMode } from "jalhyd";
 
 import { FixedResults } from "../../results/fixed-results";
 import { GraphType, VarResults } from "../../results/var-results";
@@ -40,6 +40,10 @@ export class FormResultFixedVar extends FormResult {
         for (const p of this._formBase.getDisplayedParamListFromState(ParamRadioConfig.FIX))
             if (p.symbol !== "Pr")
                 this._fixedResults.addFixedParameter(p);
+
+        for (const p of this._formBase.getDisplayedParamListFromState(ParamRadioConfig.LINK))
+            if (!p.paramDefinition.hasMultipleValues)
+                this._fixedResults.addFixedParameter(p);
     }
 
     public set graphType(t: GraphType) {
diff --git a/src/app/formulaire/fieldset-container.ts b/src/app/formulaire/fieldset-container.ts
index 1f384a140395d38231d64f8a7bb3e4a439e4a615..6d8c093b0a53a066dbfec7c21b72e0d77797618b 100644
--- a/src/app/formulaire/fieldset-container.ts
+++ b/src/app/formulaire/fieldset-container.ts
@@ -1,12 +1,8 @@
-import { Structure } from "jalhyd";
-
 import { FormulaireElement } from "./formulaire-element";
 import { FieldSet } from "./fieldset";
 import { FieldsetTemplate } from "./fieldset-template";
-import { Dependency } from "./dependency/dependency";
 import { StringMap } from "../stringmap";
 import { FormulaireNode } from "./formulaire-node";
-import { FormulaireParallelStructure } from "./definition/concrete/form-parallel-structures";
 
 export class FieldsetContainer extends FormulaireElement {
     private _templates: FieldsetTemplate[];
diff --git a/src/app/formulaire/fieldset.ts b/src/app/formulaire/fieldset.ts
index af553da83b6dd95dc4862dada03a2ecbeffe0d84..9cff04ec9478d00608ac1256ec229da70f013eea 100644
--- a/src/app/formulaire/fieldset.ts
+++ b/src/app/formulaire/fieldset.ts
@@ -1,8 +1,6 @@
 import { CalculatorType, ComputeNodeType, ParamDefinition, LoiDebit, StructureType, Props, SessionNub, Observer } from "jalhyd";
 
 import { FormulaireElement } from "./formulaire-element";
-import { Dependency } from "./dependency/dependency";
-import { DependencyConditionType } from "./dependency/dependency-condition";
 import { Field } from "./field";
 import { CheckField } from "./check-field";
 import { SelectField } from "./select-field";
@@ -11,7 +9,6 @@ import { ServiceFactory } from "../services/service-factory";
 import { ParamService } from "../services/param/param.service";
 import { FormulaireDefinition } from "./definition/form-definition";
 import { StringMap } from "../stringmap";
-import { FieldsetContainer } from "./fieldset-container";
 import { FormulaireNode } from "./formulaire-node";
 
 export class FieldSet extends FormulaireElement implements Observer {
@@ -40,16 +37,6 @@ export class FieldSet extends FormulaireElement implements Observer {
         this._props = new Props();
     }
 
-    /**
-     * formulaire parent
-     */
-    private get parentForm(): FormulaireDefinition {
-        let res = this.parent;
-        while (!(res instanceof FormulaireDefinition))
-            res = res.parent;
-        return res as FormulaireDefinition;
-
-    }
     public get sessionNub(): SessionNub {
         return this._sessionNub;
     }
@@ -242,7 +229,7 @@ export class FieldSet extends FormulaireElement implements Observer {
 
         // fin MAJ selects
 
-        this.applyDependencies(this.parentForm);
+        this.applyDependencies();
     }
 
     public parseConfig(json: {}, data?: {}) {
@@ -250,12 +237,13 @@ export class FieldSet extends FormulaireElement implements Observer {
 
         this._confId = json["id"];
 
+        const parentForm = this.parentForm as FormulaireDefinition;
         const ct: string = json["calcType"];
-        const calc_type: CalculatorType = ct == undefined ? this.parentForm.calculatorType : CalculatorType[ct];
+        const calc_type: CalculatorType = ct == undefined ? parentForm.calculatorType : CalculatorType[ct];
         this.setPropValue("calcType", calc_type);
 
         const dnt: string = json["defaultNodeType"];
-        const node_type: ComputeNodeType = dnt == undefined ? this.parentForm.nodeType : ComputeNodeType[dnt];
+        const node_type: ComputeNodeType = dnt == undefined ? parentForm.nodeType : ComputeNodeType[dnt];
         this.setPropValue("nodeType", node_type);
 
         const st: string = json["defaultStructType"];
diff --git a/src/app/formulaire/formulaire-element.ts b/src/app/formulaire/formulaire-element.ts
index 94311583903421552d7d5ae7bc7e46ede318aff6..5fe3851bdcbccf643c69c5f1e3c6b3de7399c824 100644
--- a/src/app/formulaire/formulaire-element.ts
+++ b/src/app/formulaire/formulaire-element.ts
@@ -2,10 +2,8 @@ import { FormulaireNode } from "./formulaire-node"
 import { StringMap } from "../stringmap";
 import { Dependency } from "./dependency/dependency";
 import { DependencyCondition, DependencyConditionType } from "./dependency/dependency-condition";
-import { ValueDependency } from "./dependency/value-dependency";
 import { ValueDependencyCondition } from "./dependency/value-dependency-condition";
 import { ExistenceDependency } from "./dependency/existence-dependency";
-import { FormulaireDefinition } from "./definition/form-definition";
 import { DeepFormulaireElementIterator } from "./form-iterator/deep-element-iterator";
 
 /** 
@@ -59,6 +57,17 @@ export abstract class FormulaireElement extends FormulaireNode {
         return super.kids as FormulaireElement[];
     }
 
+    /**
+     * formulaire parent
+     */
+    public get parentForm(): FormulaireNode {
+        let res = this.parent;
+        //while (!(res instanceof FormulaireDefinition))
+        while (!("calculatorName" in res)) // pour éviter de faire référence au type FormulaireDefinition, supprimer l'import correspondant et casser les dépendances circulaires d'import
+            res = res.parent;
+        return res;
+    }
+
     /**
      * analyse les dépendances d'existence
      * @param json configuration de la dépendance
@@ -127,7 +136,7 @@ export abstract class FormulaireElement extends FormulaireNode {
             }
     }
 
-    public applyDependencies(parentForm: FormulaireDefinition) {
+    public applyDependencies() {
         this.prepareExistenceDependencies();
 
         for (let d of this._dependencies) {
@@ -135,7 +144,7 @@ export abstract class FormulaireElement extends FormulaireNode {
         }
 
         for (const k of this.getKids())
-            k.applyDependencies(parentForm);
+            k.applyDependencies();
     }
 
     public printDependencies() {
diff --git a/src/app/formulaire/input-field.ts b/src/app/formulaire/input-field.ts
index 3aa574a33ebe77ae308d1000a94521e29b3f1563..933e2c626a48567c335c6807cb7abb916af457a8 100644
--- a/src/app/formulaire/input-field.ts
+++ b/src/app/formulaire/input-field.ts
@@ -1,6 +1,4 @@
 import { Field } from "./field"
-import { FormulaireDefinition } from "./definition/form-definition";
-
 
 export abstract class InputField extends Field {
     private _value: any;
diff --git a/src/app/formulaire/ngparam.ts b/src/app/formulaire/ngparam.ts
index 552594bc42c3db2db734a7d87c08b1c3fa49f523..71b4e6b68e6e83f159c6f1d533759eba5062309b 100644
--- a/src/app/formulaire/ngparam.ts
+++ b/src/app/formulaire/ngparam.ts
@@ -1,10 +1,9 @@
-import { ParamDefinition, Pair, ParamDomain, ParamValues, ParamValueMode, ParamValueIterator } from "jalhyd";
+import { ParamDefinition, Pair, ParamDomain, ParamValueMode, NumberIterator, Nub, Observer, asObservable } from "jalhyd";
 
 import { InputField } from "./input-field";
 import { Dependency } from "./dependency/dependency";
 import { DependencyConditionType } from "./dependency/dependency-condition";
 import { ValueDependencyCondition } from "./dependency/value-dependency-condition";
-import { FormulaireDefinition } from "./definition/form-definition";
 import { ServiceFactory } from "../services/service-factory";
 import { ApplicationSetupService } from "../services/app-setup/app-setup.service";
 import { StringMap } from "../stringmap";
@@ -24,13 +23,18 @@ export enum ParamRadioConfig {
     /**
      * boutons radio "paramètre fixé", "paramètre à varier" et "paramètre à calculer"
      */
-    CAL
+    CAL,
+
+    /**
+     * boutons radio "paramètre fixé", "paramètre à varier" et "paramètre à calculer", "paramètre lié"
+     */
+    LINK
 };
 
 /**
  * classe englobante de ParamDefinition (champs supplémentaires pour l'affichage, radio boutons, ...)
  */
-export class NgParameter extends InputField {
+export class NgParameter extends InputField implements Observer {
     public unit: string;
     public radioConfig: ParamRadioConfig;
 
@@ -70,7 +74,7 @@ export class NgParameter extends InputField {
     }
 
     public get radioState() {
-        switch (this._paramDef.paramValues.valueMode) {
+        switch (this._paramDef.valueMode) {
             case ParamValueMode.SINGLE:
                 return ParamRadioConfig.FIX;
 
@@ -80,6 +84,9 @@ export class NgParameter extends InputField {
 
             case ParamValueMode.CALCUL:
                 return ParamRadioConfig.CAL;
+
+            case ParamValueMode.LINK:
+                return ParamRadioConfig.LINK;
         }
     }
 
@@ -100,14 +107,50 @@ export class NgParameter extends InputField {
      * fixe la valeur du paramètre.
      * une notification préalable est envoyée pour laisser l'occasion aux objets liés de préciser le contexte
      * dans lequel cette valeur existe
-     * @param sender 
-     * @param val 
+     * @param sender
+     * @param val
      */
     public setValue(sender: any, val: number) {
-        this._paramDef.v = val;
+        this._paramDef.setValue(val, sender);
         this.notifyValueModified(sender);
     }
 
+    /**
+     * supprime un lien avec un paramètre
+     */
+    private unlinkParameter() {
+        let o = asObservable(this._paramDef.referencedObject);
+        if (this.valueMode === ParamValueMode.LINK) {
+            this._paramDef.undefineReference();
+            if (o !== undefined)
+                o.removeObserver(this);
+        }
+    }
+
+    /**
+     * crée le lien avec un paramètre
+     */
+    public linkToParameter(n: Nub, ref: string) {
+        const changed: boolean = this._paramDef.valueMode !== ParamValueMode.LINK || this._paramDef.referencedNub !== n || this._paramDef.referenceDefinition !== ref;
+        if (changed) {
+            let o = asObservable(this._paramDef.referencedObject);
+            if (o !== undefined)
+                o.removeObserver(this);
+
+            this.valueMode = ParamValueMode.LINK;
+            this._paramDef.defineReference(n, ref);
+
+            o = asObservable(this._paramDef.referencedObject);
+            if (o !== undefined)
+                o.addObserver(this); // pour être prévenu des changements de valeur de l'object référencé
+
+            this.notifyObservers({
+                "action": "valueLinkChange",
+                "value": this.getValue()
+            });
+        }
+    }
+
     get isDefined(): boolean {
         return this._paramDef.isDefined;
     }
@@ -128,42 +171,26 @@ export class NgParameter extends InputField {
     public set valueMode(m: ParamValueMode) {
         // undefined si on clique en dehors du select après l'avoir ouvert (cad sans avoir fait de sélection)
         // et au même niveau, cad à côté du bouton et non à côté du menu déroulant
-        if (m != undefined)
-            this._paramValues.valueMode = m;
-    }
-
-    /**
-     * vérifie si un min/max est valide par rapport au domaine de définition
-     */
-    private isMinMaxDomainValid(v: number): boolean {
-        if (v == undefined)
-            return false;
-
-        if (this._paramValues.valueMode == ParamValueMode.MINMAX)
-            try {
-                this.checkValue(v);
-            }
-            catch (e) {
-                return false;
-            }
-
-        return true;
-    }
-
-    private checkMinMax(min: number, max: number): boolean {
-        return this.isMinMaxDomainValid(min) && this.isMinMaxDomainValid(max) && (min < max);
+        if (m !== undefined && this._paramDef.valueMode !== m) {
+            this.unlinkParameter();
+            this._paramDef.valueMode = m;
+            this.notifyObservers({
+                "action": "valueModeChange",
+                "value": this._paramDef.getValue()
+            });
+        }
     }
 
     public checkMin(min: number): boolean {
-        return this.isMinMaxDomainValid(min) && (min < this._paramValues.max);
+        return this._paramDef.checkMin(min);
     }
 
     public checkMax(max: number): boolean {
-        return this.isMinMaxDomainValid(max) && (this._paramValues.min < max);
+        return this._paramDef.checkMax(max);
     }
 
     public get isMinMaxValid(): boolean {
-        return this.checkMinMax(this._paramValues.min, this._paramValues.max);
+        return this._paramDef.isMinMaxValid;
     }
 
     public get minValue() {
@@ -183,7 +210,7 @@ export class NgParameter extends InputField {
     }
 
     public checkStep(step: number): boolean {
-        return this.isMinMaxValid && this._paramValues.stepRefValue.intervalHasValue(step);
+        return this._paramDef.checkStep(step);
     }
 
     public get stepRefValue(): Pair {
@@ -206,59 +233,11 @@ export class NgParameter extends InputField {
         this._paramValues.valueList = l;
     }
 
-    private get isListValid(): boolean {
-        if (this._paramValues.valueList == undefined)
-            return false;
-
-        for (let v of this._paramValues.valueList)
-            try {
-                this.checkValue(v);
-            }
-            catch (e) {
-                return false;
-            }
-        return true;
-    }
-
-    private get isRangeValid(): boolean {
-        switch (this._paramValues.valueMode) {
-            case ParamValueMode.LISTE:
-                return this.isListValid;
-
-            case ParamValueMode.MINMAX:
-                return this.checkStep(this._paramValues.step);
-        }
-
-        throw new Error(`"NgParameter.isRangeValid() : valeur ${ParamValueMode[this._paramValues.valueMode]} de ParamValueMode non prise en compte`);
-    }
-
-    private get isValueValid(): boolean {
-        try {
-            const v = this._paramDef.getValue();
-            this._paramDef.checkValue(v);
-            return true;
-        }
-        catch (e) {
-            return false;
-        }
-    }
-
     public get isValid() {
-        switch (this.radioState) {
-            case ParamRadioConfig.FIX:
-                return this.isValueValid;
-
-            case ParamRadioConfig.VAR:
-                return this.isRangeValid;
-
-            case ParamRadioConfig.CAL:
-                return true;
-
-            case undefined:
-                return false;
-        }
+        if (this.radioState === undefined)
+            return false;
 
-        throw new Error(`"NgParameter.isValid() : valeur de ParamRadioConfig '${this.radioState}' (radioState) non prise en compte`);
+        return this._paramDef.isValid;
     }
 
     private static getRadioConfig(s: string) {
@@ -309,16 +288,8 @@ export class NgParameter extends InputField {
         }
     }
 
-    public get valuesIterator(): ParamValueIterator {
-        return this._paramValues.getValuesIterator();
-    }
-
-    public updateLocalisation(loc: StringMap) {
-        super.updateLocalisation(loc);
-        if (this.label == undefined) {
-            const key: string = `INFO_GRANDEUR_${this.symbol.toUpperCase()}`;
-            super.updateLocalisation(loc, key);
-        }
+    public get valuesIterator(): NumberIterator {
+        return this._paramDef.valuesIterator;
     }
 
     private paramValuesJSON(): any {
@@ -394,4 +365,14 @@ export class NgParameter extends InputField {
             }
         }
     }
+
+    // interface Observer
+
+    public update(sender: any, data: any) {
+        switch (data["action"]) {
+            case "baseparamAfterValue": // changement de valeur envoyé par l'objet référencé
+                this.notifyValueModified(sender);
+                break;
+        }
+    }
 }
diff --git a/src/app/results/remous-results.ts b/src/app/results/remous-results.ts
index 99cbddeb6fb468cab402617cd2bf0ad979b2bfa8..ec7d5ef9c09c35f803dc8e60f04bf48f4c992187 100644
--- a/src/app/results/remous-results.ts
+++ b/src/app/results/remous-results.ts
@@ -57,7 +57,7 @@ export class RemousResults extends CalculatorResults {
     /**
      * titre de la colonne du paramètre supplémentaire
      */
-    private _extraParamLabel: string;
+    private _extraParamSymbol: string;
 
     /**
      * journal de calcul
@@ -76,7 +76,7 @@ export class RemousResults extends CalculatorResults {
         this._penteFond = undefined;
         this._hautNormale = undefined;
         this._hautCritique = undefined;
-        this._extraParamLabel = undefined;
+        this._extraParamSymbol = undefined;
         this._hasFlu = false;
         this._hasTor = false;
         this._hasExtra = false;
@@ -127,7 +127,7 @@ export class RemousResults extends CalculatorResults {
                 this._hasFlu = true;
             if (!this._hasTor && re.getExtraResult("tor"))
                 this._hasTor = true;
-            if (!this._hasExtra && re.getExtraResult("tRes"))
+            if (!this._hasExtra && re.getExtraResult(this.extraParamSymbol))
                 this._hasExtra = true;
         }
 
@@ -143,13 +143,17 @@ export class RemousResults extends CalculatorResults {
         if (this._hasTor)
             keys.push("tor");
         if (this._hasExtra)
-            keys.push("tRes");
+            keys.push(this.extraParamSymbol);
         this._varResults.extraResultKeys = keys;
         this._varResults.update(true);
     }
 
-    public set extraParamLabel(l: string) {
-        this._extraParamLabel = l;
+    public get extraParamSymbol(): string {
+        return this._extraParamSymbol;
+    }
+
+    public set extraParamSymbol(l: string) {
+        this._extraParamSymbol = l;
     }
 
     public get hautBerge() {
diff --git a/src/app/results/var-results.ts b/src/app/results/var-results.ts
index 72cec056145ed3fd1bb79984b56419704c422dfd..4a39c1c9a7f8812d3f70408d155963d64bd6fe08 100644
--- a/src/app/results/var-results.ts
+++ b/src/app/results/var-results.ts
@@ -136,6 +136,6 @@ export class VarResults extends CalculatedParamResults {
 
         const intlService = ServiceFactory.instance.internationalisationService;
         for (const k of this._extraResultKeys)
-            this._extraResultHeaders.push(intlService.translateLabel(k));
+            this._extraResultHeaders.push(intlService.getExtraResLabel(k));
     }
 }
diff --git a/src/app/services/formulaire/formulaire.service.ts b/src/app/services/formulaire/formulaire.service.ts
index c51afc628ef2d2cffca040d64eca6d3e7ef6db65..c8bb1e5d2d016cb8956139710763697896b289fc 100644
--- a/src/app/services/formulaire/formulaire.service.ts
+++ b/src/app/services/formulaire/formulaire.service.ts
@@ -5,7 +5,7 @@ import "rxjs/add/operator/toPromise";
 import { decode } from "he";
 import { saveAs } from "file-saver";
 
-import { CalculatorType, EnumEx, Observable } from "jalhyd";
+import { CalculatorType, EnumEx, Observable, ParamDefinition, ParamValueMode } from "jalhyd";
 
 import { ServiceFactory } from "../service-factory";
 import { HttpService } from "../../services/http/http.service";
@@ -25,6 +25,7 @@ import { FormulairePasseBassinDimensions } from "../../formulaire/definition/con
 import { FormulairePasseBassinPuissance } from "../../formulaire/definition/concrete/form-passe-bassin-puissance";
 import { FormulaireParallelStructure } from "../../formulaire/definition/concrete/form-parallel-structures";
 import { FormulaireDever } from "../../formulaire/definition/concrete/form-dever";
+import { NgParameter } from "../../formulaire/ngparam";
 
 @Injectable()
 export class FormulaireService extends Observable {
@@ -248,6 +249,15 @@ export class FormulaireService extends Observable {
         return undefined;
     }
 
+    public getParamdefParentForm(prm: ParamDefinition): FormulaireDefinition {
+        for (const f of this._formulaires)
+            for (const p of f.allFormElements)
+                if (p instanceof NgParameter)
+                    if (p.paramDefinition.uid === prm.uid)
+                        return f;
+        return undefined;
+    }
+
     public getConfigPathPrefix(ct: CalculatorType): string {
         if (ct == undefined)
             throw "FormulaireService.getConfigPathPrefix() : invalid undefined CalculatorType"
@@ -452,4 +462,70 @@ export class FormulaireService extends Observable {
                     throw new Error(`session file : invalid key '${ks}' in session object`);
             }
     }
+
+    /**
+     * @returns liste des valeurs liables à un paramètre sous la forme d'un tableau d'objets
+     * {"param":<paramètre lié>, "nub":<Nub d'origine du paramètre lié>, "formTitle":<nom de la calculette liée au nub>}
+     * @param p paramètre qui sert de clé de recherche des paramètres liables
+     */
+    public getLinkableValues(p: NgParameter): any[] {
+        let res: any[] = [];
+
+        if (p !== undefined)
+            for (const f of this._formulaires) {
+                // nub associé au formulaire
+                const sn = f.currentSessionNub;
+
+                try {
+                    // on vérifie que le paramètre en entrée appartient au nub
+                    const np = sn.nub.getParameter(p.symbol);
+
+                    // si oui, on demande à exclure des valeurs retournées le résultat du même nom que le paramètre
+                    const ps = sn.getLinkableValues(p.paramDefinition, p.paramDefinition.uid === np.uid);
+                    for (const np of ps) {
+                        np["formTitle"] = f.calculatorName;
+                        res.push(np);
+                    }
+                }
+                catch (e) {
+                    //  p.symbol n'existe pas dans le nub testé
+                }
+            }
+
+        return res;
+    }
+
+    /**
+     * filtre les valeurs liables à un paramètre :
+     * - supprime les valeurs non affichées dans leur parent (ce n'est pas le cas par ex pour Q, Z1, Z2 dans les ouvrages enfants des ouvrages //)
+     * @param values valeurs liables (modifié par la méthode)
+     */
+    public filterLinkableValues(values: any[]): any[] {
+        // suppression des paramètres non affichés
+
+        for (let i = values.length - 1; i >= 0; i--) {
+            const v = values[i].value;
+            if (v instanceof ParamDefinition) {
+                // pour chaque paramètre...
+                const prm: ParamDefinition = v;
+
+                const parentForm: FormulaireDefinition = this.getParamdefParentForm(prm);
+
+                // ... on cherche s'il est affiché dans son parent
+                let found: boolean = false;
+                if (parentForm !== undefined)
+                    for (const fe of parentForm.allFormElements)
+                        if (fe instanceof NgParameter)
+                            if (fe.paramDefinition.uid === prm.uid) {
+                                found = true;
+                                break;
+                            }
+
+                if (!found)
+                    values.splice(i, 1);
+            }
+        }
+
+        return values;
+    }
 }
diff --git a/src/app/services/internationalisation/internationalisation.service.ts b/src/app/services/internationalisation/internationalisation.service.ts
index 00397c93119559fa0db87f5cb47b6c6a52cf117c..95b6dcbad37360c531d705f00eacdecfd213cc12 100644
--- a/src/app/services/internationalisation/internationalisation.service.ts
+++ b/src/app/services/internationalisation/internationalisation.service.ts
@@ -163,10 +163,10 @@ export class InternationalisationService extends Observable {
      */
     public localizeText(code: string) {
         if (this._Messages === undefined) {
-            return "<messages not loaded>";
+            return "*** messages not loaded: ${code} ***";
         }
         if (this._Messages[code] === undefined) {
-            return `<message not exists: ${code}>`;
+            return `*** message not exists: ${code} ***`;
         }
         return this._Messages[code];
     }
@@ -182,7 +182,7 @@ export class InternationalisationService extends Observable {
     /**
      * Traduit un libellé qui peut être un code
      */
-    public translateLabel(s: string) {
+    public getExtraResLabel(s: string) {
         const key = "INFO_EXTRARES_LIB_";
         const match = this.parseLabel(s);
         if (match) {
diff --git a/src/app/services/param/param.service.ts b/src/app/services/param/param.service.ts
index 0b5dea30679d99ce1f1f9732326d8f9885426e36..bdf292efe32cbcac376bc1be16fc9dc2e644a8c4 100644
--- a/src/app/services/param/param.service.ts
+++ b/src/app/services/param/param.service.ts
@@ -84,7 +84,7 @@ export class ParamService {
                     break;
 
                 default:
-                    throw new Error(`ComputeNodeParameters.getParameter() : symbole ${symbol} non pris en charge`);
+                    throw new Error(`ParamService.createParameter() : symbole ${symbol} non pris en charge`);
             }
         }
 
diff --git a/src/locale/error_messages.en.json b/src/locale/error_messages.en.json
index f010427d9b99184f4cb50a6616c3fb84cfa07578..985191346b4255111ce457b26b637751507cef37 100644
--- a/src/locale/error_messages.en.json
+++ b/src/locale/error_messages.en.json
@@ -33,6 +33,7 @@
     "INFO_PARAMFIELD_PARAMFIXE": "Fixed",
     "INFO_PARAMFIELD_PARAMVARIER": "Vary",
     "INFO_PARAMFIELD_PARAMCALCULER": "Calculate",
+    "INFO_PARAMFIELD_PARAMLIE": "Link",
     "INFO_PARAMFIELD_VALEURMINI": "From minimum value",
     "INFO_PARAMFIELD_VALEURMAXI": "to maximum value",
     "INFO_PARAMFIELD_PASVARIATION": "with a variation step of:",
diff --git a/src/locale/error_messages.fr.json b/src/locale/error_messages.fr.json
index 270d66e42b75aa88a66a5195b4ee6f6c357b490f..e0e56d74ca3f00cc48186a806455143a8990714a 100644
--- a/src/locale/error_messages.fr.json
+++ b/src/locale/error_messages.fr.json
@@ -39,6 +39,7 @@
     "INFO_PARAMFIELD_PARAMFIXE": "fixé",
     "INFO_PARAMFIELD_PARAMVARIER": "varier",
     "INFO_PARAMFIELD_PARAMCALCULER": "calculer",
+    "INFO_PARAMFIELD_PARAMLIE": "lié",
     "INFO_PARAMFIELD_VALEURMINI": "De la valeur minimum",
     "INFO_PARAMFIELD_VALEURMAXI": "à la valeur maximum",
     "INFO_PARAMFIELD_PASVARIATION": "avec un pas de&nbsp;:",
@@ -49,24 +50,7 @@
     "INFO_LECHAPTCALMON_TITRE": "Lechapt-Calmon",
     "INFO_REGIMEUNIFORME_TITRE": "Régime uniforme",
     "INFO_SECTIONPARAMETREE_TITRE": "Section paramétrée",
-    "INFO_GRANDEUR_Q": "Débit (m³/s)",
-    "INFO_GRANDEUR_HS": "La charge spécifique (m)",
-    "INFO_GRANDEUR_HSC": "La charge critique (m)",
-    "INFO_GRANDEUR_B": "La largeur au miroir (m)",
-    "INFO_GRANDEUR_P": "Le périmètre mouillé (m)",
-    "INFO_GRANDEUR_S": "La surface mouillée  (m²)",
-    "INFO_GRANDEUR_R": "Le rayon hydraulique  (m)",
-    "INFO_GRANDEUR_V": "La vitesse moyenne  (m/s)",
-    "INFO_GRANDEUR_FR": "Le Froude",
-    "INFO_GRANDEUR_YC": "Le tirant d'eau critique (m)",
-    "INFO_GRANDEUR_YN": "Le tirant d'eau normal (m)",
-    "INFO_GRANDEUR_YF": "Le tirant d'eau fluvial (m)",
-    "INFO_GRANDEUR_YT": "Le tirant d'eau torrentiel (m)",
-    "INFO_GRANDEUR_YCO": "Le tirant d'eau conjugué (m)",
-    "INFO_GRANDEUR_J": "La perte de charge (m)",
-    "INFO_GRANDEUR_I-J": "Variation linéaire de l'énergie spécifique (m/m)",
-    "INFO_GRANDEUR_IMP": "Impulsion (N)",
-    "INFO_GRANDEUR_TAU0": "La force tractrice (Pa)",
+
     "INFO_COURBEREMOUS_TITRE": "Courbes de remous",
     "INFO_REMOUSRESULTS_TITREJOURNAL": "Journal de calcul",
     "INFO_REMOUSRESULTS_ABSCISSE": "Abscisse (m)",
@@ -95,10 +79,27 @@
     "INFO_EXTRARES_LIB_OUVRAGE_Q": "Débit (m³/s)",
     "INFO_EXTRARES_LIB_OUVRAGE_Q_MODE": "Type d'écoulement",
     "INFO_EXTRARES_LIB_OUVRAGE_Q_REGIME": "Régime",
-    "INFO_EXTRARES_LIB_V": "V: Vitesse (m/s)",
     "INFO_EXTRARES_LIB_EC": "EC: Énergie cinétique (m)",
     "INFO_EXTRARES_LIB_CV": "Cv: Coefficient de vitesse d'approche",
     "INFO_EXTRARES_LIB_CVQT": "CV.QT: Débit corrigé (m³/s)",
+    "INFO_EXTRARES_LIB_Q": "Débit (m³/s)",
+    "INFO_EXTRARES_LIB_HS": "Charge spécifique (m)",
+    "INFO_EXTRARES_LIB_HSC": "Charge critique (m)",
+    "INFO_EXTRARES_LIB_B": "Largeur au miroir (m)",
+    "INFO_EXTRARES_LIB_P": "Périmètre mouillé (m)",
+    "INFO_EXTRARES_LIB_S": "Surface mouillée (m²)",
+    "INFO_EXTRARES_LIB_R": "Rayon hydraulique (m)",
+    "INFO_EXTRARES_LIB_V": "Vitesse moyenne (m/s)",
+    "INFO_EXTRARES_LIB_FR": "Froude",
+    "INFO_EXTRARES_LIB_YC": "Tirant d'eau critique (m)",
+    "INFO_EXTRARES_LIB_YN": "Tirant d'eau normal (m)",
+    "INFO_EXTRARES_LIB_YF": "Tirant d'eau fluvial (m)",
+    "INFO_EXTRARES_LIB_YT": "Tirant d'eau torrentiel (m)",
+    "INFO_EXTRARES_LIB_YCO": "Tirant d'eau conjugué (m)",
+    "INFO_EXTRARES_LIB_J": "Perte de charge (m)",
+    "INFO_EXTRARES_LIB_I-J": "Variation linéaire de l'énergie spécifique (m/m)",
+    "INFO_EXTRARES_LIB_IMP": "Impulsion (N)",
+    "INFO_EXTRARES_LIB_TAU0": "Force tractrice (Pa)",
 
     "INFO_EXTRARES_ENUM_OUVRAGE_Q_MODE_0": "Surface libre",
     "INFO_EXTRARES_ENUM_OUVRAGE_Q_MODE_1": "En charge",