From de9668015d65f8231f3e27701f8a8507338c26c8 Mon Sep 17 00:00:00 2001
From: "francois.grand" <francois.grand@irstea.fr>
Date: Wed, 24 Jan 2018 09:27:43 +0100
Subject: [PATCH] =?UTF-8?q?remaniement=20de=20la=20classe=20GenericInputCo?=
 =?UTF-8?q?mponent=20-=20s=C3=A9paration=20flag=20de=20validit=C3=A9=20UI/?=
 =?UTF-8?q?mod=C3=A8le=20-=20s=C3=A9paration=20message=20d'erreur=20UI/mod?=
 =?UTF-8?q?=C3=A8le=20-=20ajout=20m=C3=A9thode=20publique=20validate()=20-?=
 =?UTF-8?q?=20am=C3=A9lioration=20des=20l'initialisation?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../generic-input.component.html              |   4 +-
 .../generic-input/generic-input.component.ts  | 160 +++++++++++++-----
 2 files changed, 117 insertions(+), 47 deletions(-)

diff --git a/src/app/components/generic-input/generic-input.component.html b/src/app/components/generic-input/generic-input.component.html
index afdbb5946..a34a609b9 100644
--- a/src/app/components/generic-input/generic-input.component.html
+++ b/src/app/components/generic-input/generic-input.component.html
@@ -1,6 +1,6 @@
 <div class="md-form form-sm">
-    <input mdbActive type="text" id="form1" class="form-control" [disabled]="isDisabled" [ngModel]="uiValue" (ngModelChange)="setUIValue($event)">
+    <input mdbActive type="text" id="form1" class="form-control" [disabled]="isDisabled" [(ngModel)]="uiValue">
     <!-- on utilise [innerHTML] pour que les codes HTML comme &nbsp; soient interprétés correctement -->
     <label for="form1" [innerHTML]="_title"></label>
-    <small class="text-danger" [innerHTML]="_message"></small>
+    <small *ngIf="showError" class="text-danger" [innerHTML]="errorMessage"></small>
 </div>
\ No newline at end of file
diff --git a/src/app/components/generic-input/generic-input.component.ts b/src/app/components/generic-input/generic-input.component.ts
index a073b4a43..2077a6bbb 100644
--- a/src/app/components/generic-input/generic-input.component.ts
+++ b/src/app/components/generic-input/generic-input.component.ts
@@ -1,4 +1,6 @@
-import { Input, Output, EventEmitter, OnInit } from "@angular/core";
+import { Input, Output, EventEmitter } from "@angular/core";
+
+import { BaseComponent } from "../base/base.component";
 
 /*
 exemple de template :
@@ -12,101 +14,169 @@ exemple de template :
 
 /**
  * classe de gestion générique d'un champ de saisie avec titre, validation et message d'erreur
+ * définitions :
+ * - modèle : valeur gérée, indépendement de la façon dont elle est affichée
+ * - UI : interface utilisateur, présentation de la valeur gérée
  */
-export abstract class GenericInputComponent { //implements OnInit {
+export abstract class GenericInputComponent extends BaseComponent {
     /**
-     * enable/disable input field
+     * flag de désactivation de l'input
      */
     @Input('inputDisabled')
     private _inputDisabled: boolean = false;
 
+    /**
+     * flag d'affichage du message d'erreur
+     */
+    public showError = true;
+
     /**
      * chaîne affichée dans l'input quand aucune valeur n'est saisie
      */
     @Input('title')
     private _title: string;
 
+    /**
+     * événement signalant un changement : valeur du modèle, validité, ...
+     */
     @Output()
-    protected onChange = new EventEmitter<string>();
+    protected onChange = new EventEmitter<any>();
 
     /**
-     * validité actuelle (false si erreur dûe à la saisie ou à la valeur du modèle)
+     * valeur saisie.
+     * Cette variable n'est modifiée que lorsqu'on affecte le modèle ou que l'utilisateur fait une saisie
      */
-    private _isValid;
+    private _uiValue: string;
 
     /**
-     * message d'erreur
+     * flag de validité de la saisie dans l'UI
+     * par ex : est ce bien une valeur numérique ? n'y a-t-il que des minuscules ? etc...
      */
-    private _message: string;
+    private _isValidUI = false;
 
     /**
-     * événement de changement de la valeur du modèle
+     * flag de validité de la valeur du modèle
+     * par ex : la valeur saisie fait elle bien partie d'un domaine de définition donné ? date inférieure à une limite ? etc...
      */
-    private emitModelChanged() {
-        this.onChange.emit("value");
-    }
+    private _isValidModel = false;
 
     /**
-     * événement de changement de la valeur du modèle
+     * message d'erreur UI
      */
-    private emitValidChanged() {
-        this.onChange.emit("valid");
-    }
+    private _errorMessageUI: string;
+
+    /**
+     * message d'erreur modèle
+     */
+    private _errorMessageModel: string;
 
     private get isDisabled(): boolean {
         return this._inputDisabled;
     }
 
-    protected validateModel(): boolean {
-        let { isValid, message } = this.validateModelValue(this.getModelValue());
-        this._message = message;
-        this.setValid(isValid);
-        return isValid;
+    /**
+     * événement de changement de la validité de la saisie
+     */
+    private emitValidChanged() {
+        this.onChange.emit({ "action": "valid", "value": this.isValid });
     }
 
+    /**
+     * calcul de la validité globale du composant (UI+modèle)
+     */
     public get isValid() {
-        return this._isValid;
+        return this._isValidUI && this._isValidModel;
+    }
+
+    private setUIValid(b: boolean) {
+        const old = this.isValid;
+        this._isValidUI = b;
+        if (this.isValid != old)
+            this.emitValidChanged();
+    }
+
+    private validateUI() {
+        let { isValid, message } = this.validateUIValue(this._uiValue);
+        this._errorMessageUI = message;
+        this.setUIValid(isValid);
+        return isValid;
     }
 
-    private setValid(b: boolean) {
-        if (this._isValid != b) {
-            this._isValid = b;
+    private setModelValid(b: boolean) {
+        const old = this.isValid;
+        this._isValidModel = b;
+        if (this.isValid != old)
             this.emitValidChanged();
-        }
+    }
+
+    private validateModel() {
+        let { isValid, message } = this.validateModelValue(this.getModelValue());
+        this._errorMessageModel = message;
+        this.setModelValid(isValid);
+    }
+
+    public validate() {
+        this.validateUI();
+        this.validateModel();
+    }
+
+    /**
+     * getter du message d'erreur affiché.
+     * L'erreur de forme (UI) est prioritaire
+     */
+    private get errorMessage() {
+        if (this._errorMessageUI != undefined)
+            return this._errorMessageUI;
+        return this._errorMessageModel;
     }
 
     public get model(): any {
         return this.getModelValue();
     }
 
+    /**
+     * événement de changement de la valeur du modèle
+     */
+    private emitModelChanged() {
+        this.onChange.emit({ "action": "model", "value": this.getModelValue() });
+    }
+
+    private setAndValidateModel(v: any) {
+        this.setModelValue(v);
+        this.emitModelChanged();
+
+        this.validateModel();
+    }
+
     public set model(v: any) {
-        if (this.model !== v) {
-            this.setModelValue(v);
-            this.emitModelChanged();
-            let { isValid, message } = this.validateModelValue(v);
-            this._message = message;
-            this.setValid(isValid);
-        }
-    }
-
-    private validateUI(ui: string) {
-        let { isValid, message } = this.validateUIValue(ui);
-        this._message = message;
-        this.setValid(isValid);
-        return isValid;
+        this.setAndValidateModel(v);
+
+        // MAJ UI
+        this._uiValue = this.modelToUI(this.getModelValue());
+        this.validateUI();
     }
 
     private get uiValue() {
-        return this.modelToUI(this.getModelValue());
+        return this._uiValue;
     }
 
     /*
      * fonction appelée lorsque l'utilisateur fait une saisie
-     * @param event valeur du contrôle
+     * @param ui valeur dans le contrôle
+     */
+    private set uiValue(ui: any) {
+        this._uiValue = ui;
+        if (this.validateUI())
+            this.setAndValidateModel(this.uiToModel(ui));
+    }
+
+    /**
+     * appelé après le 1er affichage du composant
+     * @see BaseComponent
      */
-    private setUIValue(ui: any) {
-        if (this.validateUI(ui))
-            this.model = this.uiToModel(ui);
+    protected afterFirstViewChecked() {
+        this._uiValue = this.modelToUI(this.getModelValue());
+        this.validate();
     }
 
     /**
-- 
GitLab