diff --git a/src/app/app.component.html b/src/app/app.component.html
index a2dd2cf0e9b4c2f6ef118cbdeb36d9753db05e23..7c85695482c9d855809836ebe2a097d4a5bc61ee 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -1,6 +1,8 @@
 <header>
   <!--Navbar-->
   <mdb-navbar SideClass="navbar navbar-expand-sm navbar-dark indigo">
+    <!-- Ouverture du sidenav -->
+    <span style="font-size:30px;cursor:pointer;color:white" (click)="openNav()">☰</span>
 
     <!-- Navbar brand -->
     <logo>
@@ -12,61 +14,9 @@
 
       <!-- Links -->
       <ul class="navbar-nav mr-auto">
-        <!-- <li class="nav-item active">
-          <a class="nav-link waves-light" mdbRippleRadius>Home
-            <span class="sr-only">(current)</span>
-          </a>
+        <li class="nav-item" *ngFor="let c of _calculators">
+          <a class="nav-link waves-light" mdbRippleRadius [routerLink]="['/calculator/',c.uid]">{{c.title}}</a>
         </li>
-        <li class="nav-item">
-          <a class="nav-link waves-light" mdbRippleRadius>Features</a>
-        </li>
-        <li class="nav-item">
-          <a class="nav-link waves-light" mdbRippleRadius>Pricing</a>
-        </li> -->
-
-        <!-- menu calculettes -->
-        <li class="nav-item">
-          <div class="btn-group" dropdown>
-            <button dropdownToggle mdbRippleRadius type="button" class="btn btn-primary dropdown-toggle">
-              Calculettes
-              <span class="caret"></span>
-            </button>
-            <ul *dropdownMenu class="dropdown-menu" role="menu">
-              <li role="menuitem">
-                <a class="dropdown-item" (click)="setDisplayCondDistri()">Conduite distributrice</a>
-              </li>
-              <li role="menuitem">
-                <a class="dropdown-item" (click)="setDisplayLechaptCalmon()">Lechapt-Calmon</a>
-              </li>
-              <li role="menuitem">
-                <a class="dropdown-item" (click)="setDisplayRegimeUnif()">Régime uniforme</a>
-              </li>
-              <!-- <li class="divider dropdown-divider"></li> -->
-              <li role="menuitem">
-                <a class="dropdown-item" (click)="setDisplaySectionParam()">Sections paramétrées</a>
-              </li>
-              <li role="menuitem">
-                <a class="dropdown-item" (click)="setDisplayCourbeRemous()">Courbes de remous</a>
-              </li>
-            </ul>
-          </div>
-        </li>
-
-        <!-- menu langues -->
-        <li class="nav-item">
-          <div class="btn-group" dropdown>
-            <button dropdownToggle mdbRippleRadius type="button" class="btn btn-primary dropdown-toggle">
-              Langue
-              <span class="caret"></span>
-            </button>
-            <ul *dropdownMenu class="dropdown-menu" role="menu">
-              <li role="menuitem" *ngFor="let l of intlService.languages">
-                <a class="dropdown-item" (click)="selectLang(l.code)">{{l.label}}</a>
-              </li>
-            </ul>
-          </div>
-        </li>
-
       </ul>
       <!-- Links -->
 
@@ -77,14 +27,26 @@
   <!--/.Navbar-->
 </header>
 
-
 <main>
-  <div class="container-fluid">
-    <hydrocalc type="ConduiteDistributrice" *ngIf="isDisplayCondDistri()"></hydrocalc>
-    <hydrocalc type="LechaptCalmon" *ngIf="isDisplayLechaptCalmon()"></hydrocalc>
-    <hydrocalc type="RegimeUniforme" *ngIf="isDisplayRegimeUnif()"></hydrocalc>
-    <hydrocalc type="SectionParametree" *ngIf="isDisplaySectionParam()"></hydrocalc>
-    <hydrocalc type="CourbeRemous" *ngIf="isDisplayCourbeRemous()"></hydrocalc>
+  <!-- <sidenav></sidenav> -->
+  <!-- sidenav -->
+  <div class="container">
+    <div class="row">
+      <div id="mySidenav" class="sidenav">
+        <!-- ATTENTION ! pas de href="#" sous peine de rechargement de la page et réinitialisation de l'appli -->
+        <a class="closebtn" (click)="closeNav()">×</a>
+        <a (click)="newCalc()">Nouvelle calculette</a>
+        <a (click)="params()">Paramètres</a>
+      </div>
+    </div>
+  </div>
+
+  <div class="container">
+    <div class="row">
+      <div class="col 12">
+        <router-outlet></router-outlet>
+      </div>
+    </div>
   </div>
 </main>
 
diff --git a/src/app/app.component.scss b/src/app/app.component.scss
index 6cb7ce297266821d837497d81c1a328a2cadc4e5..839bbcf420c21d9be40af50730cf32f52d104627 100644
--- a/src/app/app.component.scss
+++ b/src/app/app.component.scss
@@ -1,4 +1,49 @@
 .dropdown-menu > li > a:hover {
     background-color: rgb(172, 172, 172);
     background-image: none;
-}
\ No newline at end of file
+}
+
+// sidenav
+body {
+    font-family: "Lato", sans-serif;
+}
+
+.sidenav {
+    height: 100%;
+    width: 0;
+    position: fixed;
+    z-index: 1;
+    top: 0;
+    left: 0;
+    background-color: #111;
+    overflow-x: hidden;
+    transition: 0.5s;
+    padding-top: 60px;
+}
+
+.sidenav a {
+    padding: 8px 8px 8px 32px;
+    text-decoration: none;
+    font-size: 25px;
+    color: #818181;
+    display: block;
+    transition: 0.3s;
+}
+
+.sidenav a:hover {
+    color: #f1f1f1;
+}
+
+.sidenav .closebtn {
+    position: absolute;
+    top: 0;
+    right: 25px;
+    font-size: 36px;
+    margin-left: 50px;
+}
+
+@media screen and (max-height: 450px) {
+  .sidenav {padding-top: 15px;}
+  .sidenav a {font-size: 18px;}
+}
+// sidenav
\ No newline at end of file
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index ef162d122b1860eef984044eca6d78a3a91708dc..8c93ded303c7c8a605614f0bdd9826303aa1ee58 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -1,6 +1,7 @@
 import { Component, ApplicationRef } from '@angular/core';
 //import { Component, ChangeDetectorRef, ApplicationRef, AfterViewChecked } from '@angular/core';
 //import { MdDialog } from '@angular/material';
+import { Router } from '@angular/router';
 
 import { ComputeNodeType, ComputeNode } from 'jalhyd';
 
@@ -11,26 +12,42 @@ import { Observer } from './services/observer';
 import { ErrorService } from './services/error/error.service';
 // import { AlertDialog } from './components/alert-dialog/alert-dialog.component';
 import { FormulaireService } from './services/formulaire/formulaire.service';
+import { FormulaireElement } from './formulaire/formulaire-element';
+import { FormulaireDefinition, CalculatorType } from './formulaire/formulaire-definition';
 
 
 @Component({
   selector: 'nghyd-app',
   templateUrl: './app.component.html',
-  styleUrls: ['./app.component.scss'],
-  providers: [ParamService, InternationalisationService, HttpService, FormulaireService]
+  styleUrls: ['./app.component.scss']
 })
 export class AppComponent implements Observer {
   private _currentLanguage: LanguageCode;
 
   private _displayErrorDialog: boolean = false;
 
+  private _calculators: any[] = [];
+
   // constructor(private intlService: InternationalisationService, private appRef: ApplicationRef, private dialog: MdDialog, private errorService: ErrorService) { }
-  constructor(private intlService: InternationalisationService, private appRef: ApplicationRef, private errorService: ErrorService) { }
+  constructor(private intlService: InternationalisationService,
+    private appRef: ApplicationRef,
+    private errorService: ErrorService,
+    private router: Router,
+    private formulaireService: FormulaireService
+  ) {
+    this.formulaireService.addObserver(this);
+  }
 
   private initLocale() {
     let docLocale: string = document['locale'] as string;
     this.intlService.setLocale(docLocale);
     this._currentLanguage = this.intlService.currentLanguage.code;
+
+    // process.on('unhandledRejection', (reason, p) => {
+    //   console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
+    //   // Stack Trace
+    //   console.log(reason.stack);
+    // });
   }
 
   ngOnInit() {
@@ -40,46 +57,6 @@ export class AppComponent implements Observer {
 
   private _displayedCalc: ComputeNodeType;
 
-  private isDisplayCondDistri(): boolean {
-    return this._displayedCalc == ComputeNodeType.CondDistri;
-  }
-
-  private setDisplayCondDistri() {
-    this._displayedCalc = ComputeNodeType.CondDistri;
-  }
-
-  private isDisplayLechaptCalmon(): boolean {
-    return this._displayedCalc == ComputeNodeType.LechaptCalmon;
-  }
-
-  private setDisplayLechaptCalmon() {
-    this._displayedCalc = ComputeNodeType.LechaptCalmon;
-  }
-
-  private isDisplayRegimeUnif(): boolean {
-    return this._displayedCalc == ComputeNodeType.RegimeUniforme;
-  }
-
-  private setDisplayRegimeUnif() {
-    this._displayedCalc = ComputeNodeType.RegimeUniforme;
-  }
-
-  private isDisplaySectionParam(): boolean {
-    return this._displayedCalc == ComputeNodeType.SectionParametree;
-  }
-
-  private setDisplaySectionParam() {
-    this._displayedCalc = ComputeNodeType.SectionParametree;
-  }
-
-  private isDisplayCourbeRemous(): boolean {
-    return this._displayedCalc == ComputeNodeType.CourbeRemous;
-  }
-
-  private setDisplayCourbeRemous() {
-    this._displayedCalc = ComputeNodeType.CourbeRemous;
-  }
-
   /**
    * abonnement au service d'erreurs
    */
@@ -103,14 +80,42 @@ export class AppComponent implements Observer {
 
   // interface Observer
 
-  update(data: any): void {
-    // on ouvre un dialogue avec le message d'erreur reçu
-    // if (this._displayErrorDialog) {
-    //   let dialogRef = this.dialog.open(AlertDialog);
-    //   let ad: AlertDialog = dialogRef.componentInstance;
-    //   ad.text = String(data);
-    // }
-    // else
-    console.log(data);
+  update(sender: any, data: any): void {
+    if (sender instanceof ErrorService) {
+      // on ouvre un dialogue avec le message d'erreur reçu
+      // if (this._displayErrorDialog) {
+      //   let dialogRef = this.dialog.open(AlertDialog);
+      //   let ad: AlertDialog = dialogRef.componentInstance;
+      //   ad.text = String(data);
+      // }
+      // else
+      console.log(data);
+    }
+    else if (sender instanceof FormulaireService) {
+      const f: FormulaireDefinition = data;
+      this._calculators.push(
+        {
+          "title": CalculatorType[f.calculatorType] + String(f.uid),
+          "uid": String(f.uid)
+        }
+      );
+    }
   }
+
+  // sidenav
+
+  private openNav() {
+    document.getElementById("mySidenav").style.width = "300px";
+  }
+
+  private closeNav() {
+    document.getElementById("mySidenav").style.width = "0";
+  }
+
+  private newCalc() {
+    this.closeNav();
+    this.router.navigate(['/list']);
+  }
+
+  // sidenav
 }
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 4f3a51f91e8d1bb8e76977e08884537dd0e2ebdd..b2ff7e6d137109e9587eec351472f5afb4a6be4b 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -6,7 +6,12 @@ import { HttpModule } from '@angular/http';
 import { FormsModule } from '@angular/forms'; // <-- NgModel lives here
 //import { MdInputModule, MdDialogModule } from '@angular/material';
 import { ChartModule } from 'angular2-chartjs';
+import { RouterModule, Routes } from '@angular/router';
 
+import { FormulaireService } from "./services/formulaire/formulaire.service";
+import { ParamService } from "./services/param/param.service";
+import { InternationalisationService } from "./services/internationalisation/internationalisation.service";
+import { HttpService } from "./services/http/http.service";
 import { AppComponent } from './app.component';
 import { ParamInputComponent } from './components/param-input/param-input.component';
 import { FieldSetComponent } from './components/field-set/field-set.component';
@@ -22,9 +27,20 @@ import { CalcCanvasComponent } from './components/canvas/canvas.component';
 import { SectionCanvasComponent } from './components/section-canvas/section-canvas.component';
 import { RemousResultsComponent } from './components/remous-results/remous-results.component';
 import { LogComponent } from './components/log/log.component';
+import { CalculatorListComponent } from './components/calculator-list/calculator-list.component';
+
+const appRoutes: Routes = [
+  { path: 'list', component: CalculatorListComponent },
+  { path: 'calculator/:uid', component: GenericCalculatorComponent },
+  { path: '**', component: CalculatorListComponent }
+];
 
 @NgModule({
   imports: [
+    RouterModule.forRoot(
+      appRoutes,
+      { enableTracing: true } // <-- debugging purposes only
+    ),
     BrowserModule,
     BrowserAnimationsModule,
     MDBBootstrapModule.forRoot(),
@@ -35,20 +51,23 @@ import { LogComponent } from './components/log/log.component';
     ChartModule,
     AppErrorModule
   ],
-  declarations: [
+  declarations: [ // composants, pipes et directives
     AppComponent,
     ParamInputComponent,
     FieldSetComponent,
     ParamFieldLineComponent, SelectFieldLineComponent, CheckFieldLineComponent,
     LogComponent,
+    CalculatorListComponent,
     GenericCalculatorComponent,
     // AlertDialog,
     CalculatorResultsComponent, SectionResultsComponent, RemousResultsComponent,
     CalcCanvasComponent, SectionCanvasComponent
   ],
   // entryComponents: [AlertDialog],
-  providers: [],
-  schemas: [ NO_ERRORS_SCHEMA ],
+  providers: [ // services
+    ParamService, InternationalisationService, HttpService, FormulaireService
+  ],
+  schemas: [NO_ERRORS_SCHEMA],
   bootstrap: [AppComponent]
 })
 export class AppModule { }
diff --git a/src/app/calculators/generic/calculator.component.ts b/src/app/calculators/generic/calculator.component.ts
index 48dddaea7b12d278fbc9cbc779c2dabf7e8bc97d..60f409ef56eb5d7a08cbe4d93f99b40ab38b1349 100644
--- a/src/app/calculators/generic/calculator.component.ts
+++ b/src/app/calculators/generic/calculator.component.ts
@@ -1,7 +1,5 @@
-import { Component, OnInit, DoCheck, ViewChild, Input } from "@angular/core";
-import { Response } from "@angular/http";
-import { Observable } from "rxjs/Observable";
-import "rxjs/add/operator/toPromise";
+import { Component, Inject, OnInit, DoCheck, ViewChild, Input } from "@angular/core";
+import { ActivatedRoute, ParamMap } from '@angular/router';
 
 import { ComputeNode, ComputeNodeType, ParamsEquation, Nub, acSection, RegimeUniforme, MethodeResolution, CourbeRemousParams, CourbeRemous, cLog, Result } from "jalhyd";
 
@@ -22,6 +20,7 @@ import { CalculatorResultsComponent } from "../../components/calculator-results/
 import { SectionResultsComponent } from "../../components/section-results/section-results.component";
 import { RemousResultsComponent } from "../../components/remous-results/remous-results.component";
 import { Observer } from "../../services/observer";
+import { Subscription } from "rxjs/Subscription";
 
 @Component({
     selector: 'hydrocalc',
@@ -35,16 +34,6 @@ import { Observer } from "../../services/observer";
     ]
 })
 export class GenericCalculatorComponent implements OnInit, DoCheck, Observer {
-    /**
-     * Calculator type input attribute
-     */
-    private _calculatorType: CalculatorType;
-
-    @Input()
-    private set type(s: string) {
-        this._calculatorType = CalculatorType[s];
-    }
-
     /**
      * type de noeud de calcul actuel (utilisé pour les sections, à sortir de ce composant générique)
      */
@@ -69,10 +58,8 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, Observer {
     private remousResultsComponent: RemousResultsComponent;
 
     /**
-     * objet JSON chargé depuis le fichier de traduction
+     * formulaire affiché
      */
-    private _localisation = {};
-
     private _formulaire: FormulaireDefinition;
 
     /**
@@ -90,7 +77,11 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, Observer {
      */
     private _showResultsRemous: boolean = false;
 
-    constructor(private paramService: ParamService, private httpService: HttpService, private intlService: InternationalisationService, private formulaireService: FormulaireService) {
+    constructor(private paramService: ParamService,
+        private httpService: HttpService,
+        private intlService: InternationalisationService,
+        private formulaireService: FormulaireService,
+        private route: ActivatedRoute) {
         this.intlService.addObserver(this);
     }
 
@@ -101,7 +92,7 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, Observer {
     }
 
     private get uitextTitre() {
-        switch (this._calculatorType) {
+        switch (this._formulaire.calculatorType) {
             case CalculatorType.ConduiteDistributrice:
                 return this.intlService.localizeText("INFO_CONDDISTRI_TITRE");
 
@@ -118,7 +109,7 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, Observer {
                 return this.intlService.localizeText("INFO_REMOUS_TITRE")
 
             default:
-                return "Invalid calculator type " + this._calculatorType;
+                return "Invalid calculator type " + this._formulaire.calculatorType;
         }
     }
 
@@ -126,110 +117,25 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, Observer {
         return this.intlService.localizeText("INFO_CALCULATOR_CALCULER");
     }
 
-    private loadLocalisation(): Promise<string> {
-        let ths = this;
-        let processData = function (s: string) {
-            // fermeture nécessaire pour capturer la valeur de this (undefined sinon)
-            ths._localisation = JSON.parse(s);
-            ths.formulaireService.updateLocalisation(ths._formulaire.uid, ths._localisation);
-        }
-
-        let f: string = this.formulaireService.getConfigPathPrefix(this._calculatorType) + this.intlService.currentLanguage.tag + ".json"
-        let resp: Observable<Response> = this.httpService.httpGetRequestResponse(undefined, undefined, undefined, f);
-
-        let prom = resp.map(res => res.text()).toPromise();
-        prom.then((res) => {
-            processData(res);
-        })
-
-        return prom;
-    }
-
-    private loadFormulaire(): Promise<FormulaireDefinition> {
-        if (this._formulaire == undefined) {
-            let prom = this.formulaireService.loadFormulaire(this._calculatorType);
-            prom.then(res => {
-                this._formulaire = res;
-            });
-            prom.then(
-                _ => this.loadLocalisation()
-            );
-            // prom.then(
-            //     _ => this.formulaireService.updateLocalisation(this._localisation)
-            // );
-
-            return prom;
-        }
-        return undefined;
-    }
-
-    /*
-    private getHtmlElementFromId_helper(elm: HTMLElement, id: string): HTMLElement {
-        if (elm.hasAttribute('id'))
-            if (elm.getAttribute('id') === id)
-                return elm;
-
-        let kids: NodeList = elm.childNodes;
-        for (let i = 0; i < kids.length; i++) {
-            let kid = kids[i];
-            if (kid instanceof HTMLElement) {
-                let e = this.getHtmlElementFromId_helper(<HTMLElement>kid, id);
-                if (e != undefined)
-                    return e;
-            }
-        }
-
-        return undefined;
-    }
-
-    private getHtmlElementFromId(id: string): HTMLElement {
-        let he: HTMLElement = this.elementRef.nativeElement;
-        return this.getHtmlElementFromId_helper(he, id);
-    }
-
-    private getHtmlElementValue(e: HTMLElement): string {
-        if (e instanceof HTMLSelectElement)
-            return e.value;
-        return undefined;
-    }
-    private setHtmlElementValue(e: HTMLElement, val: string) {
-        if (e instanceof HTMLInputElement)
-            e.value = val;
-    }
-    */
 
     private applyDependencies() {
         if (this._formulaire == undefined)
             return;
         this._formulaire.applyDependencies();
-
-        /*
-        for (let d of this._formulaire.dependencies) {
-            if (d instanceof ExistenceDependency) {
-            }
-            else if (d instanceof ValueDependency) {
-                let vd = <ValueDependency>d;
-                let master: HTMLElement = this.getHtmlElementFromId(d.masterElement.id);
-                if (this.getHtmlElementValue(master) == vd.masterValue) {
-                    let slave: HTMLElement = this.getHtmlElementFromId(d.slaveElement.id);
-                    this.setHtmlElementValue(slave, vd.slaveValue);
-                }
-            }
-        }
-        */
     }
 
+    private _subscription: Subscription;
+
     ngOnInit() {
-        let prom = this.loadFormulaire();
-        prom.then(
-            _ => {
-                let ssf: SelectField = <SelectField>this._formulaire.getFormulaireElementById("select_section");
-                if (ssf != undefined)
-                    this.updateSectionType(ssf.getValue());
-
-                this.applyDependencies();
-            }
-        );
+        // récupération des paramètres passés avec la route (URL)
+        this._subscription = this.route.params.subscribe(params => {
+            const uid: number = +params['uid'];
+
+            this._formulaire = this.formulaireService.getFormulaireFromId(uid);
+            let ssf: SelectField = <SelectField>this._formulaire.getFormulaireElementById("select_section");
+            if (ssf != undefined)
+                this.updateSectionType(ssf.getValue());
+        });
     }
 
     ngDoCheck() {
@@ -242,6 +148,10 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, Observer {
         // }
     }
 
+    ngOnDestroy() {
+        this._subscription.unsubscribe();
+    }
+
     /*
      * gestion des événements clic sur les radios :
      * envoi d'un message au composant parent
@@ -372,10 +282,9 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, Observer {
         this.resultsComponent.setVariableParamHeaderFromParameter(varParam, false);
         this.resultsComponent.setVariableResultHeader(computedParam["label"]);
 
-        let ssf: SelectField = <SelectField>this._formulaire.getFormulaireElementById("select_section");
-        let typeSect: ComputeNodeType = FormulaireDefinition.getComputeNodeTypeFromSection(ssf.getValue(), CalculatorType.SectionParametree);
+        let nodeType: ComputeNodeType = this.getNodeTypeFromSelectField("select_section", CalculatorType.SectionParametree);
 
-        var np: [acSection, ParamsEquation] = this.formulaireService.getSectionNubAndParameters(CalculatorType.SectionParametree, typeSect);
+        var np: [acSection, ParamsEquation] = this.formulaireService.getSectionNubAndParameters(this._formulaire, nodeType);
         let sect: acSection = np[0];
         let prms: ParamsEquation = np[1];
 
@@ -409,9 +318,8 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, Observer {
             return;
         }
 
-        let ssf: SelectField = <SelectField>this._formulaire.getFormulaireElementById("select_section");
-        let typeSect: ComputeNodeType = FormulaireDefinition.getComputeNodeTypeFromSection(ssf.getValue(), CalculatorType.SectionParametree);
-        var np: [acSection, ParamsEquation] = this.formulaireService.getSectionNubAndParameters(CalculatorType.SectionParametree, typeSect);
+        let nodeType: ComputeNodeType = this.getNodeTypeFromSelectField("select_section", CalculatorType.SectionParametree);
+        var np: [acSection, ParamsEquation] = this.formulaireService.getSectionNubAndParameters(this._formulaire, nodeType);
 
         let sect: acSection = np[0];
         let prms: ParamsEquation = np[1];
@@ -503,9 +411,8 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, Observer {
     private doComputeRemous() {
         this.remousResultsComponent.reset();
 
-        let ssf: SelectField = <SelectField>this._formulaire.getFormulaireElementById("select_section");
-        let typeSect: ComputeNodeType = FormulaireDefinition.getComputeNodeTypeFromSection(ssf.getValue(), CalculatorType.CourbeRemous);
-        var np: [acSection, ParamsEquation] = this.formulaireService.getSectionNubAndParameters(CalculatorType.CourbeRemous, typeSect, false);
+        let nodeType: ComputeNodeType = this.getNodeTypeFromSelectField("select_section", CalculatorType.CourbeRemous);
+        var np: [acSection, ParamsEquation] = this.formulaireService.getSectionNubAndParameters(this._formulaire, nodeType, false);
 
         let sect: acSection = np[0];
         let prmSect: ParamsEquation = np[1];
@@ -585,12 +492,12 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, Observer {
         this._showResultsSection = false;
         this._showResultsRemous = false;
 
-        if (this._calculatorType == CalculatorType.SectionParametree) {
+        if (this._formulaire.calculatorType == CalculatorType.SectionParametree) {
             this.doComputeSection();
             return;
         }
 
-        if (this._calculatorType == CalculatorType.CourbeRemous) {
+        if (this._formulaire.calculatorType == CalculatorType.CourbeRemous) {
             this.doComputeRemous();
             return;
         }
@@ -598,14 +505,14 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, Observer {
         let np: [Nub, ParamsEquation];
         let nub: Nub;
         let prms: ParamsEquation;
-        let rg: boolean = this._calculatorType == CalculatorType.RegimeUniforme;
+        let rg: boolean = this._formulaire.calculatorType == CalculatorType.RegimeUniforme;
         if (rg) {
-            let snp: [acSection, ParamsEquation] = this.formulaireService.getSectionNubAndParameters(CalculatorType.RegimeUniforme, this._nodeType);
+            let snp: [acSection, ParamsEquation] = this.formulaireService.getSectionNubAndParameters(this._formulaire, this._nodeType);
             nub = new RegimeUniforme(snp[0]);
             prms = snp[1];
         }
         else {
-            np = this.formulaireService.getNubAndParameters(this._calculatorType);
+            np = this.formulaireService.getNubAndParameters(this._formulaire);
             nub = np[0];
             prms = np[1];
         }
@@ -658,6 +565,67 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, Observer {
         this._showResultsFixVar = true;
     }
 
+    private getComputeNodeTypeFromSection(sect: string, calc: CalculatorType): ComputeNodeType {
+        switch (calc) {
+            case CalculatorType.SectionParametree:
+                switch (sect) {
+                    case "trapez":
+                        return ComputeNodeType.SectionTrapeze;
+
+                    case "rect":
+                        return ComputeNodeType.SectionRectangle;
+
+                    case "circ":
+                        return ComputeNodeType.SectionCercle;
+
+                    case "puiss":
+                        return ComputeNodeType.SectionPuissance;
+
+                    default:
+                        throw "getComputeNodeTypeFromSection() : section " + sect + " / type de calculette " + CalculatorType[calc] + " non pris en charge"
+                }
+
+            case CalculatorType.RegimeUniforme:
+                switch (sect) {
+                    case "trapez":
+                        return ComputeNodeType.RegimeUniformeTrapeze;
+
+                    case "rect":
+                        return ComputeNodeType.RegimeUniformeRectangle;
+
+                    case "circ":
+                        return ComputeNodeType.RegimeUniformeCercle;
+
+                    case "puiss":
+                        return ComputeNodeType.RegimeUniformePuissance;
+
+                    default:
+                        throw "getComputeNodeTypeFromSection() : section " + sect + " / type de calculette " + CalculatorType[calc] + " non pris en charge"
+                }
+
+            case CalculatorType.CourbeRemous:
+                switch (sect) {
+                    case "trapez":
+                        return ComputeNodeType.CourbeRemousTrapeze;
+
+                    case "rect":
+                        return ComputeNodeType.CourbeRemousRectangle;
+
+                    case "circ":
+                        return ComputeNodeType.CourbeRemousCercle;
+
+                    case "puiss":
+                        return ComputeNodeType.CourbeRemousPuissance;
+
+                    default:
+                        throw "getComputeNodeTypeFromSection() : section " + sect + " / type de calculette " + CalculatorType[calc] + " non pris en charge"
+                }
+
+            default:
+                throw "getComputeNodeTypeFromSection() : section " + sect + " / type de calculette " + CalculatorType[calc] + " non pris en charge"
+        }
+    }
+
     private getResultsStyleDisplay() {
         return this._showResultsFixVar ? "block" : "none";
     }
@@ -677,13 +645,10 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, Observer {
 
     // interface Observer
 
-    update(data: any): void {
-        // message de InternationalisationService
-        // const promise = this.loadLocalisation()
-        //     .then(() => {
-        //         this.formulaireService.updateLocalisation(this._localisation);
-        //     });
-        this.loadLocalisation();
+    update(sender: any, data: any): void {
+        if (sender instanceof InternationalisationService) {
+            this.formulaireService.updateLocalisation();
+        }
     }
 
     private removePrefix(s: string, prefix: string): string {
@@ -698,72 +663,18 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, Observer {
         //TODO ici on fait un cas particulier, il faudra sortir ça
 
         let type: string = this.removePrefix(val, "select_section_");
-        if (type != undefined) {
-            switch (this._calculatorType) {
-                case CalculatorType.SectionParametree:
-                    switch (type) {
-                        case "trapez":
-                            this._nodeType = ComputeNodeType.SectionTrapeze;
-                            break;
-
-                        case "rect":
-                            this._nodeType = ComputeNodeType.SectionRectangle;
-                            break;
-
-                        case "circ":
-                            this._nodeType = ComputeNodeType.SectionCercle;
-                            break;
-
-                        case "puiss":
-                            this._nodeType = ComputeNodeType.SectionPuissance;
-                            break;
-                    }
-                    break;
-
-                case CalculatorType.RegimeUniforme:
-                    switch (type) {
-                        case "trapez":
-                            this._nodeType = ComputeNodeType.RegimeUniformeTrapeze;
-                            break;
-
-                        case "rect":
-                            this._nodeType = ComputeNodeType.RegimeUniformeRectangle;
-                            break;
-
-                        case "circ":
-                            this._nodeType = ComputeNodeType.RegimeUniformeCercle;
-                            break;
-
-                        case "puiss":
-                            this._nodeType = ComputeNodeType.RegimeUniformePuissance;
-                            break;
-                    }
-                    break;
-
-                case CalculatorType.CourbeRemous:
-                    switch (type) {
-                        case "trapez":
-                            this._nodeType = ComputeNodeType.CourbeRemousTrapeze;
-                            break;
-
-                        case "rect":
-                            this._nodeType = ComputeNodeType.CourbeRemousRectangle;
-                            break;
-
-                        case "circ":
-                            this._nodeType = ComputeNodeType.CourbeRemousCercle;
-                            break;
-
-                        case "puiss":
-                            this._nodeType = ComputeNodeType.CourbeRemousPuissance;
-                            break;
-                    }
-                    break;
-
-                default:
-                    throw "GenericCalculatorComponent.updateSectionType() : section " + val + " / type de calculette " + CalculatorType[this._calculatorType] + " non pris en charge"
-            }
-        }
+        if (type != undefined)
+            this._nodeType = this.getComputeNodeTypeFromSection(type, this._formulaire.calculatorType);
+    }
+
+    private getNodeTypeFromSelectField(selectFieldId: string, calcType: CalculatorType): ComputeNodeType {
+        let ssf: SelectField = <SelectField>this._formulaire.getFormulaireElementById(selectFieldId);
+        let val = ssf.getValue();
+        let type: string = this.removePrefix(val, "select_section_");
+        if (type != undefined)
+            return this.getComputeNodeTypeFromSection(type, calcType);
+
+        return undefined;
     }
 
     /**
diff --git a/src/app/components/calculator-list/calculator-list.component.css b/src/app/components/calculator-list/calculator-list.component.css
new file mode 100644
index 0000000000000000000000000000000000000000..61c95428bd01c0e553f853c4053569dbfaf01a98
--- /dev/null
+++ b/src/app/components/calculator-list/calculator-list.component.css
@@ -0,0 +1,12 @@
+.full-page {
+    width: 100%;
+    height: 100%;
+    min-height: 100%;
+    background: red;
+    display: block;
+}
+
+.full-height { 
+    min-height: 100%;
+    height: 100%;
+}
diff --git a/src/app/components/calculator-list/calculator-list.component.html b/src/app/components/calculator-list/calculator-list.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..1b955aee56221d1e0b449c82215f014527561247
--- /dev/null
+++ b/src/app/components/calculator-list/calculator-list.component.html
@@ -0,0 +1,9 @@
+<div class="container-fluid">
+    <div class="row">
+        <div class="offset-4 col-4 mx-auto">
+            <ul *ngFor="let l of _items" class="list-group">
+                <button class="list-group-item" (click)="create(l.type)">{{l.label}}</button>
+            </ul>
+        </div>
+    </div>
+</div>
\ No newline at end of file
diff --git a/src/app/components/calculator-list/calculator-list.component.ts b/src/app/components/calculator-list/calculator-list.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d2db5a80b37a7e67982b4a9ea38c12c216cfecf1
--- /dev/null
+++ b/src/app/components/calculator-list/calculator-list.component.ts
@@ -0,0 +1,37 @@
+import { Component } from "@angular/core";
+import { Router } from '@angular/router';
+
+import { CalculatorType, FormulaireDefinition } from "../../formulaire/formulaire-definition";
+import { FormulaireService } from "../../services/formulaire/formulaire.service";
+
+class ListElement {
+    constructor(private _label: string, private _type: CalculatorType) { }
+
+    public get label() { return this._label }
+    public get type() { return this._type }
+}
+
+@Component({
+    selector: "list",
+    templateUrl: "./calculator-list.component.html",
+    styleUrls: ["./calculator-list.component.css"]
+})
+export class CalculatorListComponent {
+    private _items: ListElement[] = [];
+
+    constructor(private formulaireService: FormulaireService, private router: Router) {
+        this._items.push(new ListElement("Conduite distributrice", CalculatorType.ConduiteDistributrice));
+        this._items.push(new ListElement("Lechapt-Calmon", CalculatorType.LechaptCalmon));
+        this._items.push(new ListElement("Regime uniforme", CalculatorType.RegimeUniforme));
+        this._items.push(new ListElement("Sections paramétrées", CalculatorType.SectionParametree));
+        this._items.push(new ListElement("Courbes remous", CalculatorType.CourbeRemous));
+    }
+
+    private create(t: CalculatorType) {
+        console.log("create", t);
+        const p: Promise<FormulaireDefinition> = this.formulaireService.createFormulaire(t);
+        p.then(f => {
+            this.router.navigate(['/calculator', f.uid]);
+        });
+    }
+}
diff --git a/src/app/formulaire/formulaire-definition.ts b/src/app/formulaire/formulaire-definition.ts
index caf1ad4eedfa511eeedeb470e0b132e70ecd5662..782a500a3ca834c8001a331151137c4dfb1b4ac8 100644
--- a/src/app/formulaire/formulaire-definition.ts
+++ b/src/app/formulaire/formulaire-definition.ts
@@ -40,12 +40,12 @@ export class FormulaireDefinition {
      */
     private _defaultNodeType: ComputeNodeType;
 
-    constructor(private paramService: ParamService, private _type: CalculatorType) {
+    constructor(private paramService: ParamService, private _calcType: CalculatorType) {
         this._uid = FormulaireDefinition.uidGenerator();
     }
 
-    public get type(): CalculatorType {
-        return this._type;
+    public get calculatorType(): CalculatorType {
+        return this._calcType;
     }
 
     /**
@@ -435,35 +435,6 @@ export class FormulaireDefinition {
             console.log(d.toString());
     }
 
-    public static getComputeNodeTypeFromSection(sect: string, calc: CalculatorType): ComputeNodeType {
-        switch (calc) {
-            case CalculatorType.SectionParametree:
-                if (sect === "select_section_trapez")
-                    return ComputeNodeType.SectionTrapeze;
-                if (sect === "select_section_rect")
-                    return ComputeNodeType.SectionRectangle;
-                if (sect === "select_section_circ")
-                    return ComputeNodeType.SectionCercle;
-                if (sect === "select_section_puiss")
-                    return ComputeNodeType.SectionPuissance;
-                throw "Formulaire.getSectionType() : type de section '" + sect + "' inconnu pour la calculette " + CalculatorType[calc];
-
-            case CalculatorType.CourbeRemous:
-                if (sect === "select_section_trapez")
-                    return ComputeNodeType.CourbeRemousTrapeze;
-                if (sect === "select_section_rect")
-                    return ComputeNodeType.CourbeRemousRectangle;
-                if (sect === "select_section_circ")
-                    return ComputeNodeType.CourbeRemousCercle;
-                if (sect === "select_section_puiss")
-                    return ComputeNodeType.CourbeRemousPuissance;
-                throw "Formulaire.getSectionType() : type de section '" + sect + "' inconnu pour la calculette " + CalculatorType[calc];
-
-            default:
-                throw "Formulaire.getSectionType() : type de calculette " + CalculatorType[calc] + " non pris en charge";
-        }
-    }
-
     public isDisplayed(id: string) {
         return this.getFormulaireElementById(id).isDisplayed;
     }
diff --git a/src/app/services/formulaire/formulaire.service.ts b/src/app/services/formulaire/formulaire.service.ts
index db9439187d6c32e0f46f7a09a95f477ea5a8b9f9..0c408308aa4992e8faa8a0f2460ebba9f48b62e5 100644
--- a/src/app/services/formulaire/formulaire.service.ts
+++ b/src/app/services/formulaire/formulaire.service.ts
@@ -1,5 +1,7 @@
 import { Injectable } from "@angular/core";
 import { Response } from "@angular/http";
+import { Observable as rxObservable } from "rxjs/Observable";
+import "rxjs/add/operator/toPromise";
 
 import { ComputeNodeType, ParamsEquation, acSection, Nub, ConduiteDistrib, ConduiteDistribParams } from "jalhyd";
 import { LechaptCalmon, LechaptCalmonParams, ParamsSectionTrapez, cSnTrapez } from "jalhyd";
@@ -12,15 +14,72 @@ import { FormulaireElement } from "../../formulaire/formulaire-element";
 import { SelectField } from "../../formulaire/select-field";
 import { CheckField } from "../../formulaire/check-field";
 import { StringMap } from "../../stringmap";
+import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
+import { EnumEx } from "../../util";
+import { Observable } from "../observer";
 
 @Injectable()
-export class FormulaireService {
+export class FormulaireService extends Observable {
     private _formulaires: FormulaireDefinition[];
 
-    constructor(private paramService: ParamService, private httpService: HttpService) {
+    constructor(private paramService: ParamService,
+        private httpService: HttpService,
+        private intlService: InternationalisationService
+    ) {
+        super();
         this._formulaires = [];
     }
 
+    public get formulaires(): FormulaireDefinition[] {
+        return this._formulaires;
+    }
+
+    private loadLocalisation(calc: CalculatorType): Promise<any> {
+        let f: string = this.getConfigPathPrefix(calc) + this.intlService.currentLanguage.tag + ".json"
+        let resp: rxObservable<Response> = this.httpService.httpGetRequestResponse(undefined, undefined, undefined, f);
+
+        let prom = resp.map(res => res.text()).toPromise();
+        return prom.then((res) => {
+            let j = JSON.parse(res);
+            return j as StringMap;
+        })
+    }
+
+    /**
+     * met à jour la langue du formulaire
+     * @param formId id unique du formulaire
+     * @param localisation ensemble id-message traduit
+     */
+    private updateFormulaireLocalisation(formId: number, localisation: StringMap) {
+        for (let loc_id in localisation) {
+            let fe = this.getFormulaireElementById(formId, loc_id);
+            if (fe != undefined)
+                fe.updateLocalisation(localisation);
+        }
+    }
+
+    /**
+     * charge la localisation et met à jour la langue du formulaire
+     */
+    private loadUpdateFormulaireLocalisation(f: FormulaireDefinition): Promise<any> {
+        return this.loadLocalisation(f.calculatorType)
+            .then(localisation => {
+                this.updateFormulaireLocalisation(f.uid, localisation);
+            });
+    }
+
+    public updateLocalisation() {
+        for (const c of EnumEx.getValues(CalculatorType)) {
+            const prom: Promise<StringMap> = this.loadLocalisation(c);
+            prom.then(loc => {
+                for (const f of this._formulaires)
+                    if (f.calculatorType == c)
+                        this.updateFormulaireLocalisation(f.uid, loc);
+            }
+            );
+        }
+    }
+
     private loadConfig(form: FormulaireDefinition, ct: CalculatorType): Promise<Response> {
         let processData = function (s: string) {
             form.parseConfig(JSON.parse(s));
@@ -30,28 +89,33 @@ export class FormulaireService {
         return this.httpService.httpGetRequest(undefined, undefined, undefined, f, processData);
     }
 
-    public loadFormulaire(ct: CalculatorType): Promise<FormulaireDefinition> {
+    public createFormulaire(ct: CalculatorType): Promise<FormulaireDefinition> {
         if (ct == undefined)
-            throw "FormulaireService.getFormulaire() : invalid undefined CalculatorType"
+            throw "FormulaireService.createFormulaire() : invalid undefined CalculatorType"
 
         let f = new FormulaireDefinition(this.paramService, ct);
         this._formulaires.push(f);
-        let pr: Promise<FormulaireDefinition> = this.loadConfig(f, ct)
-            .then(res => {
-                return f;
-            });
-        return pr;
+        let prom: Promise<Response> = this.loadConfig(f, ct);
+        return prom.then(_ => {
+            return f;
+        }).then(f => {
+            this.loadUpdateFormulaireLocalisation(f);
+            return f;
+        }).then(f => {
+            f.applyDependencies();
+            return f;
+        }).then(f => {
+            this.notifyObservers(f);
+            return f;
+        });
     }
 
-    private getFormulaire(ct: CalculatorType): FormulaireDefinition {
-        if (ct == undefined)
-            throw "FormulaireService.getFormulaire() : invalid undefined CalculatorType"
-
+    public getFormulaireFromId(uid: number): FormulaireDefinition {
         for (let f of this._formulaires)
-            if (f.type == ct)
+            if (f.uid == uid)
                 return f;
 
-        throw "FormulaireService.getFormulaire() : type '" + ct + "' form is not loaded";
+        throw "FormulaireService.getFormulaire() : unkown form id " + uid;
     }
 
     private getFormulaireElementById(formId: number, elemId: string): FormulaireElement {
@@ -90,22 +154,8 @@ export class FormulaireService {
         return undefined;
     }
 
-    /**
-     * met à jour la langue du formulaire
-     * @param formId id unique du formulaire
-     * @param localisation ensemble id-message traduit
-     */
-    public updateLocalisation(formId: number, localisation: StringMap) {
-        for (let loc_id in localisation) {
-            let fe = this.getFormulaireElementById(formId, loc_id);
-            if (fe != undefined)
-                fe.updateLocalisation(localisation);
-        }
-    }
-
-    public getNubAndParameters(ct: CalculatorType): [Nub, ParamsEquation] {
-        let f = this.getFormulaire(ct);
-        switch (ct) {
+    public getNubAndParameters(f: FormulaireDefinition): [Nub, ParamsEquation] {
+        switch (f.calculatorType) {
             case CalculatorType.ConduiteDistributrice:
                 {
                     let Q: number = f.getParameterValue("Q"); // débit Q
@@ -133,15 +183,13 @@ export class FormulaireService {
                 }
 
             default:
-                throw "FormulaireService.getNubAndParameters() : valeur de CalculatorType " + ct + " non implémentée"
+                throw "FormulaireService.getNubAndParameters() : valeur de CalculatorType " + f.calculatorType + " non implémentée"
         }
     }
 
-    public getSectionNubAndParameters(ct: CalculatorType, nt: ComputeNodeType, getY: boolean = true): [acSection, ParamsEquation] {
-        let f: FormulaireDefinition = this.getFormulaire(ct);
+    public getSectionNubAndParameters(f: FormulaireDefinition, nt: ComputeNodeType, getY: boolean = true): [acSection, ParamsEquation] {
 
         // bief
-        // let Ks = f.getNodeParameterValue(nt, "Ks"); // Strickler
         let Ks = f.getParameterValue("Ks"); // Strickler
         let If: number = f.getParameterValue("If"); // Pente du fond
         let YB: number = f.getParameterValue("YB"); // Hauteur de berge
@@ -208,7 +256,7 @@ export class FormulaireService {
         }
     }
 
-    public getConfigPathPrefix(ct: CalculatorType): string {
+    private getConfigPathPrefix(ct: CalculatorType): string {
         if (ct == undefined)
             throw "FormulaireService.getConfigPathPrefix() : invalid undefined CalculatorType"
 
diff --git a/src/app/services/observer.ts b/src/app/services/observer.ts
index b916f71b3bcad3a926d71dd508451309a2f34f42..8933a7c60f2430d9f4f56f15487d6c07f740a358 100644
--- a/src/app/services/observer.ts
+++ b/src/app/services/observer.ts
@@ -1,10 +1,8 @@
 export interface Observer {
-    update(data: any): void;
+    update(sender: any, data: any): void;
 }
 
-// class Observable<T> {
 export class Observable {
-    // private data: T;
 
     private _observers: Observer[];
 
@@ -18,7 +16,7 @@ export class Observable {
 
     public notifyObservers(data: any) {
         for (let o of this._observers) {
-            o.update(data);
+            o.update(this, data);
         }
     }
 }
diff --git a/src/app/util.ts b/src/app/util.ts
index b6777e733836be1e975f1cd23242b5bb7fbb3197..297bda30c95628265de4e338e22e363a23853612 100644
--- a/src/app/util.ts
+++ b/src/app/util.ts
@@ -4,4 +4,35 @@ export function logObject(obj: {}, m?: string) {
         console.log(JSON.stringify(obj));
     else
         console.log(m + " " + JSON.stringify(obj));
+}
+
+/**
+ * classe d'itérateurs pour les enums
+ * cf. https://stackoverflow.com/questions/21293063/how-to-programmatically-enumerate-an-enum-type-in-typescript-0-9-5#21294925
+ */
+export class EnumEx {
+    /**
+     * retourne les noms et les valeurs d'un enum
+     */
+    static getNamesAndValues<T extends number>(e: any) {
+        return EnumEx.getNames(e).map(n => ({ name: n, value: e[n] as T }));
+    }
+
+    /**
+     * retourne les noms d'un enum
+     */
+    static getNames(e: any) {
+        return EnumEx.getObjValues(e).filter(v => typeof v === "string") as string[];
+    }
+
+    /**
+     * retourne les valeurs d'un enum
+     */
+    static getValues<T extends number>(e: any) {
+        return EnumEx.getObjValues(e).filter(v => typeof v === "number") as T[];
+    }
+
+    private static getObjValues(e: any): (number | string)[] {
+        return Object.keys(e).map(k => e[k]);
+    }
 }
\ No newline at end of file
diff --git a/src/index.html b/src/index.html
index 799c40343da9ac23d68c5079ded026142dd2a805..aa3fc3c95b653b683c8e0696eebb5339af57dbeb 100644
--- a/src/index.html
+++ b/src/index.html
@@ -2,9 +2,9 @@
 <html lang="en">
 
 <head>
+  <base href="/">
   <meta charset="utf-8">
   <title>ngHyd - Calculettes hydrauliques</title>
-  <base href="/">
 
   <meta name="viewport" content="width=device-width, initial-scale=1">
   <!-- <link rel="icon" type="image/x-icon" href="favicon.ico"> -->