diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index a9a6a7a518a32150af911479c4d73d52a2aab175..2c3ab0eee9d93d8575220b3922c5a26a119c0cb4 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -102,10 +102,13 @@ export class AppComponent implements OnInit, OnDestroy, Observer {
           const f: FormulaireDefinition = data["form"];
           this._calculators.push(
             {
-              "title": CalculatorType[f.calculatorType] + String(f.uid),
+              "title": f.calculatorName,
               "uid": String(f.uid)
             }
           );
+
+          // abonnement en tant qu'observateur du nouveau formulaire
+          f.addObserver(this);
           break;
 
         case "invalidFormId":
@@ -113,25 +116,49 @@ export class AppComponent implements OnInit, OnDestroy, Observer {
           break;
 
         case "closeForm":
-          const uid: number = data["formId"];
-          this.closeCalculator(uid);
+          const form: FormulaireDefinition = data["form"];
+          this.closeCalculator(form);
           break;
       }
     }
     else if (sender instanceof InternationalisationService) {
       this.updateLocale();
     }
+    else if (sender instanceof FormulaireDefinition) {
+      switch (data["action"]) {
+        case "nameChanged":
+          this.updateCalculatorTitle(sender, data["name"]);
+          break;
+      }
+    }
   }
 
-  private closeCalculator(formId: number) {
-    // recherche de la calculette correspondante à formId
-
-    const closedIndex = this._calculators.reduce((resultIndex, calc, currIndex) => {
+  private getCalculatorIndexFromId(formId: number) {
+    const index = this._calculators.reduce((resultIndex, calc, currIndex) => {
       if (resultIndex == -1 && calc["uid"] == formId)
         resultIndex = currIndex;
       return resultIndex;
     }, -1);
 
+    return index;
+  }
+
+  private updateCalculatorTitle(f: FormulaireDefinition, title: string) {
+    const formIndex = this.getCalculatorIndexFromId(f.uid);
+    this._calculators[formIndex]["title"] = title;
+  }
+
+  private closeCalculator(form: FormulaireDefinition) {
+    const formId: number = form.uid;
+
+    // désabonnement en tant qu'observateur
+
+    form.removeObserver(this);
+
+    // recherche de la calculette correspondante à formId
+
+    const closedIndex = this.getCalculatorIndexFromId(formId);
+
     /*
      * détermination de la nouvelle calculette à afficher : 
      * - celle après celle supprimée
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 8b33c6b0929c3398bc015dc8ae43007fa5d38f32..5b51e56b9fdac8b4ed72b4b00f0f3c6b18b97bce 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -30,6 +30,7 @@ import { CalculatorResultsComponent } from './components/calculator-results/calc
 import { FixedVarResultsComponent } from './components/fixedvar-results/fixedvar-results.component';
 import { SectionResultsComponent } from './components/section-results/section-results.component';
 import { GenericCalculatorComponent } from './components/generic-calculator/calculator.component';
+import { CalculatorNameComponent } from './components/generic-calculator/calc-name.component';
 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';
@@ -74,7 +75,7 @@ const appRoutes: Routes = [
     CalculatorListComponent,
     ApplicationSetupComponent,
     BaseParamInputComponent,
-    GenericCalculatorComponent,
+    GenericCalculatorComponent, CalculatorNameComponent,
     // AlertDialog,
     CalculatorResultsComponent, FixedVarResultsComponent, SectionResultsComponent, RemousResultsComponent,
     ResultsGraphComponent, GraphTypeSelectComponent,
diff --git a/src/app/components/calculator-list/calculator-list.component.ts b/src/app/components/calculator-list/calculator-list.component.ts
index 509b10a1cca46798f73746670865e3c1fb6a5068..39993acec5b5111059a4ffac072ff28b0a39a109 100644
--- a/src/app/components/calculator-list/calculator-list.component.ts
+++ b/src/app/components/calculator-list/calculator-list.component.ts
@@ -1,13 +1,17 @@
-import { Component } from "@angular/core";
+import { Component, OnInit } from "@angular/core";
 import { Router } from '@angular/router';
 
 import { CalculatorType, FormulaireDefinition } from "../../formulaire/formulaire-definition";
 import { FormulaireService } from "../../services/formulaire/formulaire.service";
 import { InternationalisationService } from '../../services/internationalisation/internationalisation.service';
-import { OnInit } from "@angular/core/src/metadata/lifecycle_hooks";
+import { EnumEx } from "../../util";
 
 class ListElement {
-    constructor(private _label: string, private _type: CalculatorType) { }
+    private _label: string;
+
+    constructor(private _type: CalculatorType, formulaireService: FormulaireService) {
+        this._label = formulaireService.getLocalisedTitleFromCalculatorType(_type);
+    }
 
     public get label() { return this._label }
     public get type() { return this._type }
@@ -31,13 +35,8 @@ export class CalculatorListComponent implements OnInit {
 
     private updateLocale() {
         this._items = [];
-        this._items.push(new ListElement(this.intlService.localizeText("INFO_CONDDISTRI_TITRE"), CalculatorType.ConduiteDistributrice));
-        this._items.push(new ListElement(this.intlService.localizeText("INFO_LECHAPT_TITRE"), CalculatorType.LechaptCalmon));
-        this._items.push(new ListElement(this.intlService.localizeText("INFO_REGUNI_TITRE"), CalculatorType.RegimeUniforme));
-        this._items.push(new ListElement(this.intlService.localizeText("INFO_SECTPARAM_TITRE"), CalculatorType.SectionParametree));
-        this._items.push(new ListElement(this.intlService.localizeText("INFO_REMOUS_TITRE"), CalculatorType.CourbeRemous));
-        this._items.push(new ListElement(this.intlService.localizeText("INFO_PABDIM_TITRE"), CalculatorType.PabDimensions));
-        this._items.push(new ListElement(this.intlService.localizeText("INFO_PABPUISS_TITRE"), CalculatorType.PabPuissance));
+        for (let t of EnumEx.getValues(CalculatorType))
+            this._items.push(new ListElement(t, this.formulaireService));
     }
 
     private create(t: CalculatorType) {
diff --git a/src/app/components/generic-calculator/calc-name.component.ts b/src/app/components/generic-calculator/calc-name.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ba100ae758300ba8fada34ba5b6fc8c00325b521
--- /dev/null
+++ b/src/app/components/generic-calculator/calc-name.component.ts
@@ -0,0 +1,86 @@
+import { Component, Input } from "@angular/core";
+import { GenericInputComponent } from "../generic-input/generic-input.component";
+import { FormulaireDefinition } from "../../formulaire/formulaire-definition";
+
+@Component({
+    selector: 'calc-name',
+    templateUrl: "../generic-input/generic-input.component.html",
+})
+export class CalculatorNameComponent extends GenericInputComponent {
+    /**
+     * formulaire géré
+     */
+    private _form: FormulaireDefinition;
+
+    public set formulaire(f: FormulaireDefinition) {
+        this._form = f;
+        this.updateAndValidateUI();
+    }
+
+    /**
+     * retourne la valeur du modèle
+     */
+    protected getModelValue(): any {
+        if (this._form == undefined)
+            return undefined;
+        return this._form.calculatorName;
+    }
+
+    /**
+     * affecte la valeur du modèle
+     */
+    protected setModelValue(v: any) {
+        this._form.calculatorName = v;
+        this.updateAndValidateUI();
+    }
+
+    /**
+     * valide une valeur de modèle : est ce une valeur acceptable ? (par ex, nombre dans un intervalle, valeur dans une liste, ...)
+     * @param v valide la valeur du modèle
+     * @returns isValid : true si la valeur est valide, false sinon
+     * @returns message : message d'erreur
+     */
+    protected validateModelValue(v: any): { isValid: boolean, message: string } {
+        let msg = undefined;
+        let valid = false;
+
+        if (!(typeof (v) == "string") || v.length < 1)
+            msg = "Veuillez entrer un nom";
+        else
+            valid = true;
+
+        return { isValid: valid, message: msg };
+    }
+
+    /**
+     * convertit le modèle en valeur affichable par l'UI
+     */
+    protected modelToUI(v: any): string {
+        return v;
+    }
+
+    /**
+     * valide une valeur saisie dans l'UI (forme de la saisie : est ce bien une date, un nombre, ...)
+     * @param ui valide la valeur saisie
+     * @returns isValid : true si la valeur est valide, false sinon
+     * @returns message : message d'erreur
+     */
+    protected validateUIValue(ui: string): { isValid: boolean, message: string } {
+        let valid: boolean = false;
+        let msg: string = undefined;
+
+        if (ui == undefined || ui.length < 1)
+            msg = "Veuillez entrer un nom";
+        else
+            valid = true;
+
+        return { isValid: valid, message: msg };
+    }
+
+    /**
+     * convertit une valeur saisie dans l'UI en valeur affectable au modèle
+     */
+    protected uiToModel(ui: string): any {
+        return ui;
+    }
+}
diff --git a/src/app/components/generic-calculator/calculator.component.html b/src/app/components/generic-calculator/calculator.component.html
index 4621678b126844f314ff0ee52942342f0674fbdb..8c7d3b1c44e41940e90563601ce17598b500b286 100644
--- a/src/app/components/generic-calculator/calculator.component.html
+++ b/src/app/components/generic-calculator/calculator.component.html
@@ -11,6 +11,13 @@
     </div>
 </div>
 
+<!-- nom de la calculette -->
+<div class="row">
+    <div class="col-6">
+        <calc-name title="Nom de la calculette"></calc-name>
+    </div>
+</div>
+
 <div class="row">
     <div [ngClass]="(hasResults) ? 'col-12 col-lg-6' : 'col-12'">
         <div class="container-fluid">
diff --git a/src/app/components/generic-calculator/calculator.component.ts b/src/app/components/generic-calculator/calculator.component.ts
index 603629af5fe3d51b3d90bbc297003c26befc1711..e5a2b2abcf5247cd2c27e13c19e2fff49de4512e 100644
--- a/src/app/components/generic-calculator/calculator.component.ts
+++ b/src/app/components/generic-calculator/calculator.component.ts
@@ -11,9 +11,9 @@ import { Observer } from "../../services/observer";
 import { Subscription } from "rxjs/Subscription";
 import { FieldSetComponent } from "../field-set/field-set.component";
 import { BaseComponent } from "../base/base.component";
+import { CalculatorNameComponent } from "./calc-name.component";
 import { SelectEntry } from "../../formulaire/select-entry";
 
-
 @Component({
     selector: 'hydrocalc',
     templateUrl: "./calculator.component.html",
@@ -48,6 +48,12 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
     @ViewChild(CalculatorResultsComponent)
     private resultsComponent: CalculatorResultsComponent;
 
+    /**
+     * composant "nom de la calculette"
+     */
+    @ViewChild(CalculatorNameComponent)
+    private _calculatorNameComponent: CalculatorNameComponent;
+
     /**
      * formulaire affiché
      */
@@ -97,31 +103,7 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
 
     private get uitextTitre() {
         if (this.hasForm)
-            switch (this._formulaire.calculatorType) {
-                case CalculatorType.ConduiteDistributrice:
-                    return this.intlService.localizeText("INFO_CONDDISTRI_TITRE");
-
-                case CalculatorType.LechaptCalmon:
-                    return this.intlService.localizeText("INFO_LECHAPT_TITRE");
-
-                case CalculatorType.RegimeUniforme:
-                    return this.intlService.localizeText("INFO_REGUNI_TITRE");
-
-                case CalculatorType.SectionParametree:
-                    return this.intlService.localizeText("INFO_SECTPARAM_TITRE");
-
-                case CalculatorType.CourbeRemous:
-                    return this.intlService.localizeText("INFO_REMOUS_TITRE")
-
-                case CalculatorType.PabDimensions:
-                    return this.intlService.localizeText("INFO_PABDIM_TITRE")
-
-                case CalculatorType.PabPuissance:
-                    return this.intlService.localizeText("INFO_PABPUISS_TITRE")
-
-                default:
-                    return "Invalid calculator type " + this._formulaire.calculatorType;
-            }
+            return this.formulaireService.getLocalisedTitleFromCalculatorType(this._formulaire.calculatorType);
 
         return undefined;
     }
@@ -244,6 +226,7 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
                     const uid: number = +data["formId"];
                     this.setForm(this.formulaireService.getFormulaireFromId(uid));
                     this.resultsComponent.formulaire = this._formulaire;
+                    this._calculatorNameComponent.formulaire = this._formulaire;
                     if (this._formulaire != undefined)
                         this._formulaire.updateNodeType();
                     break;
diff --git a/src/app/components/generic-input/generic-input.component.ts b/src/app/components/generic-input/generic-input.component.ts
index 64487d22e1a613383b5701d6065739292adedb19..4458edfeb30f6e089c01276e147713d2db9478ef 100644
--- a/src/app/components/generic-input/generic-input.component.ts
+++ b/src/app/components/generic-input/generic-input.component.ts
@@ -180,7 +180,7 @@ export abstract class GenericInputComponent extends BaseComponent {
      * @see BaseComponent
      */
     protected afterFirstViewChecked() {
-         this.updateAndValidateUI();
+        this.updateAndValidateUI();
         this.validateModel();
     }
 
diff --git a/src/app/formulaire/formulaire-definition.ts b/src/app/formulaire/formulaire-definition.ts
index 30b7f7323f403a118349fe271a314045a9b77ea5..4f1ebfc4cb532c2d0871a76449cf17d4f33c6658 100644
--- a/src/app/formulaire/formulaire-definition.ts
+++ b/src/app/formulaire/formulaire-definition.ts
@@ -78,6 +78,11 @@ export class FormulaireDefinition extends Observable {
      */
     private _remousResults: RemousResults;
 
+    /**
+     * nom de de la calculette
+     */
+    private _calculatorName: string;
+
     constructor(
         private _calcType: CalculatorType,
         private paramService: ParamService,
@@ -1230,4 +1235,16 @@ export class FormulaireDefinition extends Observable {
             res = res && fs.isValid;
         return res;
     }
+
+    public get calculatorName() {
+        return this._calculatorName;
+    }
+
+    public set calculatorName(n: string) {
+        this._calculatorName = n;
+        this.notifyObservers({
+            "action": "nameChanged",
+            "name": n
+        });
+    }
 }
diff --git a/src/app/services/formulaire/formulaire.service.ts b/src/app/services/formulaire/formulaire.service.ts
index 6bc3746c9b23721211deb35ef32bcc4e025be6a7..54323a114b63aa8133884027df041b9a3f65cb9e 100644
--- a/src/app/services/formulaire/formulaire.service.ts
+++ b/src/app/services/formulaire/formulaire.service.ts
@@ -81,6 +81,34 @@ export class FormulaireService extends Observable {
         }
     }
 
+    public getLocalisedTitleFromCalculatorType(type: CalculatorType) {
+        switch (type) {
+            case CalculatorType.ConduiteDistributrice:
+                return this.intlService.localizeText("INFO_CONDDISTRI_TITRE");
+
+            case CalculatorType.LechaptCalmon:
+                return this.intlService.localizeText("INFO_LECHAPT_TITRE");
+
+            case CalculatorType.RegimeUniforme:
+                return this.intlService.localizeText("INFO_REGUNI_TITRE");
+
+            case CalculatorType.SectionParametree:
+                return this.intlService.localizeText("INFO_SECTPARAM_TITRE");
+
+            case CalculatorType.CourbeRemous:
+                return this.intlService.localizeText("INFO_REMOUS_TITRE")
+
+            case CalculatorType.PabDimensions:
+                return this.intlService.localizeText("INFO_PABDIM_TITRE")
+
+            case CalculatorType.PabPuissance:
+                return this.intlService.localizeText("INFO_PABPUISS_TITRE")
+
+            default:
+                return "Invalid calculator type " + type;
+        }
+    }
+
     private loadConfig(form: FormulaireDefinition, ct: CalculatorType): Promise<Response> {
         let processData = function (s: string) {
             form.parseConfig(JSON.parse(s));
@@ -95,6 +123,7 @@ export class FormulaireService extends Observable {
             throw "FormulaireService.createFormulaire() : invalid undefined CalculatorType"
 
         let f = new FormulaireDefinition(ct, this.paramService, this.intlService, this.appSetupService);
+        f.calculatorName = this.getLocalisedTitleFromCalculatorType(ct) + " (" + f.uid + ")";
         this._formulaires.push(f);
         let prom: Promise<Response> = this.loadConfig(f, ct);
         return prom.then(_ => {
@@ -192,7 +221,7 @@ export class FormulaireService extends Observable {
 
             this.notifyObservers({
                 "action": "closeForm",
-                "formId": uid
+                "form": form
             });
         }
     }