From bab3aa26d79579713a9539ac0c246529a81187b6 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Mon, 28 Jan 2019 11:42:41 +0100
Subject: [PATCH] Remplacement de la modale de chargement de session par
 angular-material

---
 e2e/sidenav.po.ts                             |   5 +-
 package-lock.json                             |  58 +++---
 package.json                                  |   1 +
 src/app/app.component.scss                    |   1 +
 src/app/app.component.ts                      |  28 +--
 src/app/app.module.ts                         |  20 +-
 .../dialog-load-session.component.html        |  46 +++++
 .../dialog-load-session.component.scss        |  19 ++
 .../dialog-load-session.component.ts          |  97 ++++++++++
 .../load-calculator-anchor.directive.ts       |  28 ---
 .../load-calculator.component.html            |  24 ---
 .../load-calculator.component.ts              | 172 ------------------
 .../services/formulaire/formulaire.service.ts |   2 +-
 src/locale/messages.en.json                   |   3 +
 src/locale/messages.fr.json                   |   3 +
 15 files changed, 231 insertions(+), 276 deletions(-)
 create mode 100644 src/app/components/dialog-load-session/dialog-load-session.component.html
 create mode 100644 src/app/components/dialog-load-session/dialog-load-session.component.scss
 create mode 100644 src/app/components/dialog-load-session/dialog-load-session.component.ts
 delete mode 100644 src/app/components/load-calculator/load-calculator-anchor.directive.ts
 delete mode 100644 src/app/components/load-calculator/load-calculator.component.html
 delete mode 100644 src/app/components/load-calculator/load-calculator.component.ts

diff --git a/e2e/sidenav.po.ts b/e2e/sidenav.po.ts
index 07dd1cea1..2b3f469d4 100644
--- a/e2e/sidenav.po.ts
+++ b/e2e/sidenav.po.ts
@@ -8,11 +8,12 @@ export class SideNav {
 
   getFileInput() {
     // tslint:disable-next-line:quotemark
-    return element(by.css('load-calc input[type="file"]'));
+    return element(by.css('dialog-load-session input[type="file"]'));
   }
 
   getFileLoadButton() {
-    return element(by.css("load-calc .modal-footer button.btn-success"));
+    // tslint:disable-next-line:quotemark
+    return element(by.css('dialog-load-session button[type="submit"]'));
   }
 
   async clickLoadSessionButton() {
diff --git a/package-lock.json b/package-lock.json
index 24e1035bd..e70363d5d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -133,7 +133,7 @@
       "dependencies": {
         "source-map": {
           "version": "0.5.6",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz",
+          "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz",
           "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=",
           "dev": true
         },
@@ -431,7 +431,7 @@
         },
         "load-json-file": {
           "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
+          "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
           "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
           "dev": true,
           "requires": {
@@ -1739,7 +1739,7 @@
         },
         "util": {
           "version": "0.10.3",
-          "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
+          "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz",
           "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
           "dev": true,
           "requires": {
@@ -1866,7 +1866,7 @@
         },
         "supports-color": {
           "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+          "resolved": "http://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
           "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
           "dev": true
         }
@@ -3884,7 +3884,7 @@
         },
         "source-map": {
           "version": "0.2.0",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz",
+          "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz",
           "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=",
           "dev": true,
           "optional": true,
@@ -6579,7 +6579,7 @@
         },
         "resolve": {
           "version": "1.1.7",
-          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
+          "resolved": "http://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
           "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
           "dev": true
         },
@@ -7994,7 +7994,7 @@
     },
     "load-json-file": {
       "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
+      "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
       "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
       "dev": true,
       "optional": true,
@@ -8420,7 +8420,7 @@
     },
     "meow": {
       "version": "3.7.0",
-      "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
+      "resolved": "http://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
       "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
       "dev": true,
       "optional": true,
@@ -8796,6 +8796,14 @@
       "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==",
       "dev": true
     },
+    "ngx-material-file-input": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/ngx-material-file-input/-/ngx-material-file-input-1.1.0.tgz",
+      "integrity": "sha512-+HJOJIp6uE21QU6nHOYjT9/Hd1/JXx57CLTh8XlqDi9/HGfa5SPlvgjkn/RcrjJxTMz+lnGBMhi8RmEs6bbENA==",
+      "requires": {
+        "tslib": "^1.9.0"
+      }
+    },
     "ngx-md": {
       "version": "7.0.0",
       "resolved": "https://registry.npmjs.org/ngx-md/-/ngx-md-7.0.0.tgz",
@@ -8862,7 +8870,7 @@
       "dependencies": {
         "semver": {
           "version": "5.3.0",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
+          "resolved": "http://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
           "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=",
           "dev": true,
           "optional": true
@@ -8960,7 +8968,7 @@
         },
         "supports-color": {
           "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+          "resolved": "http://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
           "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
           "dev": true,
           "optional": true
@@ -9432,7 +9440,7 @@
     },
     "os-locale": {
       "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
+      "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
       "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
       "dev": true,
       "optional": true,
@@ -9790,7 +9798,7 @@
     },
     "path-browserify": {
       "version": "0.0.0",
-      "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz",
+      "resolved": "http://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz",
       "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=",
       "dev": true
     },
@@ -10564,7 +10572,7 @@
     },
     "raw-loader": {
       "version": "0.5.1",
-      "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-0.5.1.tgz",
+      "resolved": "http://registry.npmjs.org/raw-loader/-/raw-loader-0.5.1.tgz",
       "integrity": "sha1-DD0L6u2KAclm2Xh793goElKpeao=",
       "dev": true
     },
@@ -11068,7 +11076,7 @@
       "dependencies": {
         "source-map": {
           "version": "0.4.4",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
+          "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
           "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
           "dev": true,
           "optional": true,
@@ -11263,7 +11271,7 @@
     },
     "sha.js": {
       "version": "2.4.11",
-      "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
+      "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
       "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
       "dev": true,
       "requires": {
@@ -11922,7 +11930,7 @@
         },
         "readable-stream": {
           "version": "2.3.6",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+          "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
           "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
           "dev": true,
           "requires": {
@@ -11937,7 +11945,7 @@
         },
         "string_decoder": {
           "version": "1.1.1",
-          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+          "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
           "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
           "dev": true,
           "requires": {
@@ -12072,13 +12080,13 @@
         },
         "sax": {
           "version": "0.5.8",
-          "resolved": "https://registry.npmjs.org/sax/-/sax-0.5.8.tgz",
+          "resolved": "http://registry.npmjs.org/sax/-/sax-0.5.8.tgz",
           "integrity": "sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE=",
           "dev": true
         },
         "source-map": {
           "version": "0.1.43",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz",
+          "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz",
           "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=",
           "dev": true,
           "requires": {
@@ -12121,7 +12129,7 @@
     },
     "tar": {
       "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz",
+      "resolved": "http://registry.npmjs.org/tar/-/tar-2.2.1.tgz",
       "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=",
       "dev": true,
       "optional": true,
@@ -12359,7 +12367,7 @@
         },
         "readable-stream": {
           "version": "2.3.6",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+          "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
           "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
           "dev": true,
           "requires": {
@@ -12374,7 +12382,7 @@
         },
         "string_decoder": {
           "version": "1.1.1",
-          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+          "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
           "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
           "dev": true,
           "requires": {
@@ -12653,7 +12661,7 @@
     },
     "tty-browserify": {
       "version": "0.0.0",
-      "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
+      "resolved": "http://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
       "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=",
       "dev": true
     },
@@ -13005,7 +13013,7 @@
     },
     "vm-browserify": {
       "version": "0.0.4",
-      "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz",
+      "resolved": "http://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz",
       "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=",
       "dev": true,
       "requires": {
@@ -13110,7 +13118,7 @@
         },
         "source-map": {
           "version": "0.4.4",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
+          "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
           "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
           "dev": true,
           "requires": {
diff --git a/package.json b/package.json
index e693cb633..b1f6f77ad 100644
--- a/package.json
+++ b/package.json
@@ -41,6 +41,7 @@
     "he": "^1.2.0",
     "jalhyd": "file:../jalhyd",
     "mathjax": "^2.7.5",
+    "ngx-material-file-input": "^1.1.0",
     "ngx-md": "^7.0.0",
     "rxjs": "^6.3.3",
     "rxjs-compat": "^6.3.3",
diff --git a/src/app/app.component.scss b/src/app/app.component.scss
index 55d5653c8..9501b1f43 100644
--- a/src/app/app.component.scss
+++ b/src/app/app.component.scss
@@ -63,6 +63,7 @@ button:focus {
 }
 
 .calculator-button {
+    // @TODO remplacer par du Flex ?
     width: 15.8%; // 6 larger tabs
     @media screen and (max-width: 1200px) {
         width: 14.5%; // 6 tabs
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index c5f1bc136..40aecafd2 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -12,13 +12,12 @@ import { ServiceFactory } from "./services/service-factory";
 import { ParamService } from "./services/param/param.service";
 import { ApplicationSetupService } from "./services/app-setup/app-setup.service";
 import { HttpService } from "./services/http/http.service";
-import { LoadCalcDialogAnchorDirective } from "./components/load-calculator/load-calculator-anchor.directive";
-import { LoadCalculatorComponent } from "./components/load-calculator/load-calculator.component";
 import { nghydDateRev } from "../date_revision";
 
 import { MatSidenav, MatToolbar, MatDialog } from "@angular/material";
 import { DialogConfirmEmptySessionComponent } from "./components/dialog-confirm-empty-session/dialog-confirm-empty-session.component";
-import { DialogSaveSessionComponent } from './components/dialog-save-session/dialog-save-session.component';
+import { DialogLoadSessionComponent } from "./components/dialog-load-session/dialog-load-session.component";
+import { DialogSaveSessionComponent } from "./components/dialog-save-session/dialog-save-session.component";
 
 
 @Component({
@@ -54,9 +53,6 @@ export class AppComponent implements OnInit, OnDestroy, Observer {
 
   private _innerWidth: number;
 
-  @ViewChild(LoadCalcDialogAnchorDirective)
-  private appLoadCalcDialogAnchor: LoadCalcDialogAnchorDirective;
-
   /**
    * composant actuellement affiché par l'élément <router-outlet>
    */
@@ -72,7 +68,8 @@ export class AppComponent implements OnInit, OnDestroy, Observer {
     private formulaireService: FormulaireService,
     private httpService: HttpService,
     private confirmEmptySessionDialog: MatDialog,
-    private saveSessionDialog: MatDialog
+    private saveSessionDialog: MatDialog,
+    private loadSessionDialog: MatDialog
   ) {
     ServiceFactory.instance.applicationSetupService = appSetupService;
     ServiceFactory.instance.paramService = paramService;
@@ -371,13 +368,16 @@ export class AppComponent implements OnInit, OnDestroy, Observer {
 
   public loadSession() {
     // création du dialogue de sélection des formulaires à sauver
-    const compRef: ComponentRef<LoadCalculatorComponent> = this.appLoadCalcDialogAnchor.createDialog();
-
-    const prom: Promise<any[]> = compRef.instance.run();
-    prom.then(list => {
-      this.formulaireService.loadSession(compRef.instance.selectedFile, compRef.instance.calculators);
-      compRef.destroy();
-    }).catch(err => { });
+    const dialogRef = this.loadSessionDialog.open(
+      DialogLoadSessionComponent,
+      { disableClose: true }
+    );
+    dialogRef.afterClosed().subscribe(result => {
+      console.log("closed avayk", result);
+      if (result) {
+        this.formulaireService.loadSession(result.file, result.calculators);
+      }
+    });
   }
 
   public getDateRevision(): string[] {
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index fc0d5b75a..9f2bebe38 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -1,12 +1,15 @@
 import { BrowserModule } from "@angular/platform-browser";
 import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
 import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core";
+
 import { MatButtonModule, MatCheckboxModule, MatIconModule, MatSelectModule, MatTabsModule, MatSidenavModule,
   MatToolbarModule, MatMenuModule, MatDialogModule, MatFormFieldModule, MatInputModule, MatListModule } from "@angular/material";
+import { MaterialFileInputModule } from "ngx-material-file-input";
+
 import { FlexLayoutModule } from "@angular/flex-layout";
 import { MDBBootstrapModule } from "angular-bootstrap-md";
 import { HttpClientModule } from "@angular/common/http";
-import { FormsModule } from "@angular/forms"; // <-- NgModel lives here
+import { FormsModule, ReactiveFormsModule } from "@angular/forms"; // <-- NgModel lives here
 import { ChartModule } from "angular2-chartjs";
 import { RouterModule, Routes } from "@angular/router";
 import { NgxMdModule } from "ngx-md";
@@ -49,13 +52,11 @@ import { ResultElementBaseComponent } from "./components/result-element/result-e
 import { HorizontalResultElementComponent } from "./components/result-element/horizontal-result-element.component";
 import { VerticalResultElementComponent } from "./components/result-element/vertical-result-element.component";
 import { LogEntryComponent } from "./components/log-entry/log-entry.component";
-import { LoadCalculatorComponent } from "./components/load-calculator/load-calculator.component";
-import { LoadCalcDialogAnchorDirective } from "./components/load-calculator/load-calculator-anchor.directive";
 import { ParamLinkComponent } from "./components/param-link/param-link.component";
 
 import { DialogConfirmEmptySessionComponent } from "./components/dialog-confirm-empty-session/dialog-confirm-empty-session.component";
 import { DialogConfirmCloseCalcComponent } from "./components/dialog-confirm-close-calc/dialog-confirm-close-calc.component";
-// import { DialogLoadSessionComponent } from "./components/dialog-load-session/dialog-load-session.component";
+import { DialogLoadSessionComponent } from "./components/dialog-load-session/dialog-load-session.component";
 import { DialogSaveSessionComponent } from "./components/dialog-save-session/dialog-save-session.component";
 
 const appRoutes: Routes = [
@@ -67,15 +68,17 @@ const appRoutes: Routes = [
 
 @NgModule({
   imports: [
+    FormsModule, // <-- import the FormsModule before binding with [(ngModel)]
+    ReactiveFormsModule,
     BrowserAnimationsModule,
     BrowserModule,
     ChartModule,
     HttpClientModule,
     FlexLayoutModule,
-    FormsModule, // <-- import the FormsModule before binding with [(ngModel)]
     MatButtonModule,
     MatCheckboxModule,
     MatDialogModule,
+    MaterialFileInputModule,
     MatFormFieldModule,
     MatIconModule,
     MatInputModule,
@@ -106,7 +109,7 @@ const appRoutes: Routes = [
     CheckFieldLineComponent,
     DialogConfirmCloseCalcComponent,
     DialogConfirmEmptySessionComponent,
-    // DialogLoadSessionComponent,
+    DialogLoadSessionComponent,
     DialogSaveSessionComponent,
     FieldSetComponent,
     FieldsetContainerComponent,
@@ -115,8 +118,6 @@ const appRoutes: Routes = [
     GenericCalculatorComponent,
     GraphTypeSelectComponent,
     HorizontalResultElementComponent,
-    LoadCalcDialogAnchorDirective,
-    LoadCalculatorComponent,
     LogComponent,
     LogEntryComponent,
     NgParamInputComponent,
@@ -140,8 +141,7 @@ const appRoutes: Routes = [
     DialogConfirmCloseCalcComponent,
     DialogConfirmEmptySessionComponent,
     DialogSaveSessionComponent,
-    // DialogLoadSessionComponent,
-    LoadCalculatorComponent
+    DialogLoadSessionComponent
   ],
   providers: [ // services
     ApplicationSetupService,
diff --git a/src/app/components/dialog-load-session/dialog-load-session.component.html b/src/app/components/dialog-load-session/dialog-load-session.component.html
new file mode 100644
index 000000000..75e143061
--- /dev/null
+++ b/src/app/components/dialog-load-session/dialog-load-session.component.html
@@ -0,0 +1,46 @@
+<h1 mat-dialog-title [innerHTML]="uitextLoadSessionTitle"></h1>
+
+<form [formGroup]="loadSessionForm">
+
+    <!-- <input type="file" required [(ngModel)]="file"
+      name="fileSelection" #fileSelection="ngModel"> -->
+
+
+  <div mat-dialog-content>
+
+    <mat-form-field>
+      <ngx-mat-file-input id="session-file-input" #sessionFile formControlName="file"[placeholder]="uitextLoadSessionFilename"
+        (change)="onFileSelected($event)"></ngx-mat-file-input>
+      <button mat-icon-button matSuffix *ngIf="!sessionFile.empty" (click)="sessionFile.clear($event)">
+        <mat-icon>clear</mat-icon>
+      </button>
+    </mat-form-field>
+
+    <div class="cb-container">
+      <mat-checkbox [name]="c.uid" *ngFor="let c of calculators" [(ngModel)]="c.selected" [ngModelOptions]="{standalone: true}">
+        {{ c.title }}
+      </mat-checkbox>
+    </div>
+
+    <div class="btn-container">
+      <button mat-raised-button (click)="selectAll()" [disabled]="calculators.length === 0">
+        {{ uitextAll }}
+      </button>
+      <button mat-raised-button (click)="selectNone()" [disabled]="calculators.length === 0">
+        {{ uitextNone }}
+      </button>
+    </div>
+  </div>
+
+  <div mat-dialog-actions>
+    <button mat-raised-button color="primary" [mat-dialog-close]="false" cdkFocusInitial>
+      {{ uitextCancel }}
+    </button>
+    <button mat-raised-button type="submit" color="warn" (click)="loadSession()"
+      [disabled]="(loadSessionForm.invalid || ! atLeastOneCheckboxSelected)">
+
+      {{ uitextLoad }}
+    </button>
+  </div>
+
+</form>
diff --git a/src/app/components/dialog-load-session/dialog-load-session.component.scss b/src/app/components/dialog-load-session/dialog-load-session.component.scss
new file mode 100644
index 000000000..e612ad2cf
--- /dev/null
+++ b/src/app/components/dialog-load-session/dialog-load-session.component.scss
@@ -0,0 +1,19 @@
+mat-form-field {
+    width: 100%;
+}
+
+.cb-container {
+    margin-bottom: 5px;
+
+    mat-checkbox {
+        display: block;
+    }
+}
+
+.btn-container {
+    margin-bottom: 20px;
+
+    button {
+        margin-right: 5px;
+    }
+}
diff --git a/src/app/components/dialog-load-session/dialog-load-session.component.ts b/src/app/components/dialog-load-session/dialog-load-session.component.ts
new file mode 100644
index 000000000..c07202737
--- /dev/null
+++ b/src/app/components/dialog-load-session/dialog-load-session.component.ts
@@ -0,0 +1,97 @@
+import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material";
+import { Inject, Component } from "@angular/core";
+import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
+import { FormGroup, FormBuilder, Validators } from "@angular/forms";
+import { ServiceFactory } from "../../services/service-factory";
+
+@Component({
+    selector: "dialog-load-session",
+    templateUrl: "dialog-load-session.component.html",
+    styleUrls: ["dialog-load-session.component.scss"]
+})
+export class DialogLoadSessionComponent {
+
+    public calculators: any[] = [];
+
+    public file: any;
+
+    public loadSessionForm: FormGroup;
+
+    constructor(
+      public dialogRef: MatDialogRef<DialogLoadSessionComponent>,
+      private intlService: InternationalisationService,
+      private fb: FormBuilder,
+      @Inject(MAT_DIALOG_DATA) public data: any
+    ) {
+      this.loadSessionForm = this.fb.group({
+        file: [null, Validators.required]
+      });
+    }
+
+    public selectAll() {
+      for (const c of this.calculators) {
+        c.selected = true;
+      }
+    }
+
+    public selectNone() {
+      for (const c of this.calculators) {
+        c.selected = false;
+      }
+    }
+
+    public onFileSelected(event: any) {
+      if (event.target.files && event.target.files.length) {
+        this.file = event.target.files[0];
+
+        const formService = ServiceFactory.instance.formulaireService;
+        formService.calculatorInfosFromSessionFile(this.file).then(
+          calcInfos => {
+            this.calculators = calcInfos;
+            for (const n of this.calculators) {
+                n.selected = true;
+            }
+          }
+        );
+      }
+    }
+
+    public loadSession() {
+      this.dialogRef.close({
+        calculators: this.calculators,
+        file: this.file
+      });
+    }
+
+    public get atLeastOneCheckboxSelected() {
+      let ok = false;
+      for (const c of this.calculators) {
+        ok = (ok || c.selected);
+      }
+      return ok;
+    }
+
+    public get uitextLoad() {
+      return this.intlService.localizeText("INFO_OPTION_LOAD");
+    }
+
+    public get uitextCancel() {
+      return this.intlService.localizeText("INFO_OPTION_CANCEL");
+    }
+
+    public get uitextAll() {
+      return this.intlService.localizeText("INFO_OPTION_ALL");
+    }
+
+    public get uitextNone() {
+      return this.intlService.localizeText("INFO_OPTION_NONE");
+    }
+
+    public get uitextLoadSessionFilename() {
+      return this.intlService.localizeText("INFO_DIALOG_LOAD_SESSION_FILENAME");
+    }
+
+    public get uitextLoadSessionTitle() {
+      return this.intlService.localizeText("INFO_DIALOG_LOAD_SESSION_TITLE");
+    }
+}
diff --git a/src/app/components/load-calculator/load-calculator-anchor.directive.ts b/src/app/components/load-calculator/load-calculator-anchor.directive.ts
deleted file mode 100644
index 8d9b713ed..000000000
--- a/src/app/components/load-calculator/load-calculator-anchor.directive.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { Directive, ComponentFactoryResolver, ComponentFactory, ComponentRef } from "@angular/core";
-
-import { ViewContainerRef } from "@angular/core";
-import { LoadCalculatorComponent } from "./load-calculator.component";
-
-@Directive({
-    selector: "[appLoadCalcDialogAnchor]"
-})
-export class LoadCalcDialogAnchorDirective {
-    constructor(
-        private viewContainer: ViewContainerRef,
-        private componentFactoryResolver: ComponentFactoryResolver
-    ) { }
-
-    public createDialog(): ComponentRef<LoadCalculatorComponent> {
-        this.viewContainer.clear();
-
-        const compFactory: ComponentFactory<LoadCalculatorComponent>
-            = this.componentFactoryResolver.resolveComponentFactory(LoadCalculatorComponent);
-        const compRef: ComponentRef<LoadCalculatorComponent> = this.viewContainer.createComponent(compFactory);
-
-        // compRef.instance.confirmResult.subscribe(() => {
-        // compRef.destroy();
-        // });
-
-        return compRef;
-    }
-}
diff --git a/src/app/components/load-calculator/load-calculator.component.html b/src/app/components/load-calculator/load-calculator.component.html
deleted file mode 100644
index e5233cc47..000000000
--- a/src/app/components/load-calculator/load-calculator.component.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<div mdbModal #loadDialog="mdb-modal" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true" [config]="{backdrop: false, ignoreBackdropClick: true,show:true}">
-    <div class="modal-dialog" role="document">
-        <div class="modal-content">
-            <div class="modal-header">
-                <h4 class="modal-title w-100" id="myModalLabel">{{ uitextDialogTitle }}</h4>
-            </div>
-            <div class="modal-body">
-                <div>
-                    <input type="file" #fileSelector multiple="false" accept="*.json" (change)="onFileSelect()">
-                </div>
-                <div *ngFor="let c of calculators">
-                    <input type="checkbox" value={{c.uid}} checked={{isSelected(c)}} (change)="onCheckCalc($event)">{{ c.title }}
-                </div>
-                <button *ngIf="showSelectButtons" type="button" class="btn btn-mdb-color waves-light" (click)="selectAll()" mdbRippleRadius>{{ uitextSelectAll }}</button>
-                <button *ngIf="showSelectButtons" type="button" class="btn btn-mdb-color waves-light" (click)="deselectAll()" mdbRippleRadius>{{ uitextDeselectAll }}</button>
-            </div>
-            <div class="modal-footer">
-                <button type="button" class="btn btn-danger relative waves-light" (click)="loadDialog.hide();cancelLoad()" mdbRippleRadius>{{ uitextCancel }}</button>
-                <button type="button" class="btn btn-success waves-light" [disabled]="disableLoadButton" (click)="loadDialog.hide();confirmLoad()"
-                    mdbRippleRadius>{{ uitextLoad }}</button>
-            </div>
-        </div>
-    </div>
-</div>
\ No newline at end of file
diff --git a/src/app/components/load-calculator/load-calculator.component.ts b/src/app/components/load-calculator/load-calculator.component.ts
deleted file mode 100644
index 655d3baf2..000000000
--- a/src/app/components/load-calculator/load-calculator.component.ts
+++ /dev/null
@@ -1,172 +0,0 @@
-import { Component, EventEmitter, ViewChild } from "@angular/core";
-
-import { ServiceFactory } from "../../services/service-factory";
-import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
-
-@Component({
-    selector: "load-calc",
-    templateUrl: "./load-calculator.component.html"
-})
-export class LoadCalculatorComponent {
-    @ViewChild("fileSelector") fileSelector;
-
-    private _selectFile: File;
-
-    /**
-     * liste des calculettes affichées. Forme des objets :
-     * "title": nom de la calculette
-     * "selected": flag de sélection pour la sauvegarde
-     */
-    private _calculators: any[];
-
-    /**
-     * événement émis lors du clic sur "annuler"/"charger"
-     * utilisé par la promise de gestion de la confirmation/annulation de la sauvegarde
-     */
-    private _confirmResult = new EventEmitter();
-
-    // services
-    private intlService: InternationalisationService;
-
-    constructor() {
-        this.intlService = ServiceFactory.instance.internationalisationService;
-    }
-
-    public get uitextDialogTitle() {
-        return "Charger des calculettes";
-    }
-
-    public get uitextCancel() {
-        // return this.intlService.localizeText("INFO_OPTION_NO");
-        return "Annuler";
-    }
-
-    public get uitextLoad() {
-        // return this.intlService.localizeText("INFO_OPTION_NO");
-        return "Charger";
-    }
-
-    public get uitextSelectAll() {
-        return "Toutes";
-    }
-
-    public get uitextDeselectAll() {
-        return "Aucune";
-    }
-
-    /**
-     * calcule l'état du bouton charger
-     */
-    public get disableLoadButton() {
-        // pas de fichier sélectionné -> bouton grisé
-        if (this._selectFile === undefined) {
-            return true;
-        }
-
-        // au moins une calculette sélectionnée -> dégrisé
-        if (this._calculators !== undefined) {
-            for (const c of this._calculators) {
-                if (c.selected) {
-                    return false;
-                }
-            }
-        }
-
-        // grisé sinon
-        return true;
-    }
-
-    public run(): Promise<any[]> {
-        // promise de gestion de la confirmation/annulation de la sauvegarde
-        return new Promise((resolve, reject) => {
-            this._confirmResult.subscribe((confirm) => {
-                if (confirm) {
-                    resolve(this._calculators);
-                } else {
-                    reject("canceled");
-                }
-            });
-        });
-    }
-
-    public get showSelectButtons(): boolean {
-        return this._calculators && this._calculators.length !== 0;
-    }
-
-    private getSelectedFile(): File {
-        const files: { [key: string]: File } = this.fileSelector.nativeElement.files;
-        for (const key in files) {
-            if (!isNaN(Number(key))) {
-                return files[key];
-            }
-        }
-        return undefined;
-    }
-
-    public onFileSelect() {
-        const formService = ServiceFactory.instance.formulaireService;
-        this._selectFile = this.getSelectedFile();
-        if (this._selectFile !== undefined) {
-            formService.calculatorInfosFromSessionFile(this._selectFile).then(
-                calcInfos => {
-                    this._calculators = calcInfos;
-                    for (const n of this._calculators) {
-                        n["selected"] = true;
-                    }
-                });
-        }
-    }
-
-    public get selectedFile(): File {
-        return this._selectFile;
-    }
-
-    public get calculators(): any[] {
-        return this._calculators;
-    }
-
-    private isSelected(c: any) {
-        return c.selected ? "checked" : undefined;
-    }
-
-    private onCheckCalc(event: any) {
-        for (const c of this._calculators) {
-            if (c.uid === event.target.value) {
-                c.selected = event.target.checked;
-                break;
-            }
-        }
-    }
-
-    public selectAll() {
-        for (const c of this._calculators) {
-            c.selected = true;
-        }
-    }
-
-    public deselectAll() {
-        for (const c of this._calculators) {
-            c.selected = false;
-        }
-    }
-
-    private set confirmed(b: boolean) {
-        setTimeout(() => {
-            this._confirmResult.next(b);
-        }, 0);
-    }
-
-    /**
-     * appelé quand on clique sur annuler
-     */
-    public cancelLoad() {
-        this.confirmed = false;
-    }
-
-    /**
-     * appelé quand on clique sur charger
-     */
-    public confirmLoad() {
-        this.confirmed = true;
-    }
-}
diff --git a/src/app/services/formulaire/formulaire.service.ts b/src/app/services/formulaire/formulaire.service.ts
index 56d4a30c4..0aecac54e 100644
--- a/src/app/services/formulaire/formulaire.service.ts
+++ b/src/app/services/formulaire/formulaire.service.ts
@@ -362,7 +362,7 @@ export class FormulaireService extends Observable {
     /**
      * charge une session en tenant compte des calculettes sélectionnées
      * @param f fichier session
-     * @param formInfos infos sur les calculettes @see LoadCalculatorComponent._calculators
+     * @param formInfos infos sur les calculettes @see DialogLoadSessionComponent.calculators
      */
     public loadSession(f: File, formInfos: any[]) {
         this.readSingleFile(f).then(s => {
diff --git a/src/locale/messages.en.json b/src/locale/messages.en.json
index 1e219d124..cd678334a 100644
--- a/src/locale/messages.en.json
+++ b/src/locale/messages.en.json
@@ -48,6 +48,8 @@
     "INFO_EXTRARES_ENUM_STRUCTUREFLOWREGIME_1": "Partially submerged",
     "INFO_EXTRARES_ENUM_STRUCTUREFLOWREGIME_2": "Submerged",
     "INFO_EXTRARES_ENUM_STRUCTUREFLOWREGIME_3": "Zero flow",
+    "INFO_DIALOG_LOAD_SESSION_FILENAME": "Choose a file",
+    "INFO_DIALOG_LOAD_SESSION_TITLE": "Load calculator modules",
     "INFO_DIALOG_SAVE_SESSION_FILENAME": "File name",
     "INFO_DIALOG_SAVE_SESSION_TITLE": "Save calculator modules",
     "INFO_EMPTY_SESSION_DIALOGUE_TEXT": "Warning ! All open calculators will be lost. Continue ?",
@@ -119,6 +121,7 @@
     "INFO_OPTION_NO": "No",
     "INFO_OPTION_YES": "Yes",
     "INFO_OPTION_CANCEL": "Cancel",
+    "INFO_OPTION_LOAD": "Load",
     "INFO_OPTION_SAVE": "Save",
     "INFO_OPTION_ALL": "All",
     "INFO_OPTION_ALL_F": "All",
diff --git a/src/locale/messages.fr.json b/src/locale/messages.fr.json
index 274d78b9c..912d86b5a 100644
--- a/src/locale/messages.fr.json
+++ b/src/locale/messages.fr.json
@@ -48,6 +48,8 @@
     "INFO_EXTRARES_ENUM_STRUCTUREFLOWREGIME_1": "Partiellement noyé",
     "INFO_EXTRARES_ENUM_STRUCTUREFLOWREGIME_2": "Noyé",
     "INFO_EXTRARES_ENUM_STRUCTUREFLOWREGIME_3": "Débit nul",
+    "INFO_DIALOG_LOAD_SESSION_FILENAME": "Choisir un fichier",
+    "INFO_DIALOG_LOAD_SESSION_TITLE": "Charger des modules de calcul",
     "INFO_DIALOG_SAVE_SESSION_FILENAME": "Nom de fichier",
     "INFO_DIALOG_SAVE_SESSION_TITLE": "Enregistrer les modules de calcul",
     "INFO_EMPTY_SESSION_DIALOGUE_TEXT": "Attention&nbsp;! Toutes les calculettes ouvertes seront perdues. Continuer&nbsp;?",
@@ -119,6 +121,7 @@
     "INFO_OPTION_NO": "Non",
     "INFO_OPTION_YES": "Oui",
     "INFO_OPTION_CANCEL": "Annuler",
+    "INFO_OPTION_LOAD": "Charger",
     "INFO_OPTION_SAVE": "Enregistrer",
     "INFO_OPTION_ALL": "Tous",
     "INFO_OPTION_ALL_F": "Toutes",
-- 
GitLab