From ad22310e08789f86ba5820056a98c41a1a61d20a Mon Sep 17 00:00:00 2001 From: "francois.grand" <francois.grand@irstea.fr> Date: Wed, 24 Jan 2018 09:46:20 +0100 Subject: [PATCH] =?UTF-8?q?Mise=20en=20place=20de=20la=20d=C3=A9sactivatio?= =?UTF-8?q?n=20du=20bouton=20"calculer"=20-=20bouton=20"calculer"=20affich?= =?UTF-8?q?=C3=A9=20en=20rouge=20(en=20plus=20d'=C3=AAtre=20d=C3=A9sactiv?= =?UTF-8?q?=C3=A9)=20lorsque=20la=20calculette=20est=20invalide=20-=20NgPa?= =?UTF-8?q?ramInputComponent=20:=20le=20message=20d'erreur=20n'est=20pas?= =?UTF-8?q?=20affich=C3=A9=20si=20le=20param=C3=A8tre=20est=20=C3=A0=20var?= =?UTF-8?q?ier=20ou=20=C3=A0=20calculer=20-=20propagation=20et=20synth?= =?UTF-8?q?=C3=A8se=20de=20l'=C3=A9v=C3=A9nement=20de=20validit=C3=A9=20(c?= =?UTF-8?q?alcul=C3=A9e=20par=20l'UI)=20depuis=20les=20inputs=20en=20remon?= =?UTF-8?q?tant=20vers=20le=20formulaire=20-=20composants=20GenericCalcula?= =?UTF-8?q?torComponent,=20FieldSetComponent,=20ParamValuesComponent=20et?= =?UTF-8?q?=20GenericInputComponent=20bas=C3=A9s=20sur=20BaseComponent=20d?= =?UTF-8?q?e=20fa=C3=A7on=20=C3=A0=20pouvoir=20g=C3=A9rer=20leur=20initial?= =?UTF-8?q?isation=20(calculer=20la=20validit=C3=A9=20initiale=20de=20l'ar?= =?UTF-8?q?bre=20des=20composants=20de=20la=20calculatrice,=20valeurs=20da?= =?UTF-8?q?ns=20les=20champs,=20...)=20-=20les=20composants=20NgParamMinCo?= =?UTF-8?q?mponent,=20NgParamMaxComponent,=20NgParamStepComponent=20et=20V?= =?UTF-8?q?alueListComponent=20g=C3=A8rent=20directement=20une=20instance?= =?UTF-8?q?=20de=20NgParameter=20-=20d=C3=A9placement=20de=20ValueListComp?= =?UTF-8?q?onent=20dans=20son=20propre=20fichier?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/app.module.ts | 3 +- .../field-set/field-set.component.html | 2 +- .../field-set/field-set.component.ts | 66 +++- .../calculator.component.html | 5 +- .../calculator.component.ts | 80 +++- .../param-field-line.component.html | 4 +- .../param-field-line.component.ts | 91 ++++- .../param-values/ngparam-max.component.ts | 29 +- .../param-values/ngparam-min.component.ts | 31 +- .../param-values/ngparam-step.component.ts | 42 +- .../param-values/param-values.component.html | 10 +- .../param-values/param-values.component.ts | 362 ++++++++++-------- .../param-values/value-list.component.ts | 95 +++++ 13 files changed, 553 insertions(+), 267 deletions(-) create mode 100644 src/app/components/param-values/value-list.component.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 9c8da9ef4..8b33c6b09 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -20,7 +20,8 @@ import { ParamFieldLineComponent } from './components/param-field-line/param-fie import { NgParamMinComponent } from './components/param-values/ngparam-min.component'; import { NgParamMaxComponent } from './components/param-values/ngparam-max.component'; import { NgParamStepComponent } from './components/param-values/ngparam-step.component'; -import { ParamValuesComponent, ValueListComponent } from './components/param-values/param-values.component'; +import { ParamValuesComponent } from './components/param-values/param-values.component'; +import { ValueListComponent } from './components/param-values/value-list.component'; import { SelectFieldLineComponent } from './components/select-field-line/select-field-line.component'; import { CheckFieldLineComponent } from './components/check-field-line/check-field-line.component'; // import { AlertDialog } from './components/alert-dialog/alert-dialog.component'; diff --git a/src/app/components/field-set/field-set.component.html b/src/app/components/field-set/field-set.component.html index 9539cd8f0..463113c33 100644 --- a/src/app/components/field-set/field-set.component.html +++ b/src/app/components/field-set/field-set.component.html @@ -15,7 +15,7 @@ --> <ng-template ngFor let-p [ngForOf]="fields"> - <param-field-line *ngIf="p.isInput" [param]=p (onRadio)=onRadioClick($event)> + <param-field-line *ngIf="p.isInput" [param]=p (onRadio)=onRadioClick($event) (onValid)=onParamLineValid()> </param-field-line> <select-field-line *ngIf="p.isSelect" [param]=p (onSelectChange)=onSelectChanged($event)> diff --git a/src/app/components/field-set/field-set.component.ts b/src/app/components/field-set/field-set.component.ts index 817875d6e..108f4724d 100644 --- a/src/app/components/field-set/field-set.component.ts +++ b/src/app/components/field-set/field-set.component.ts @@ -1,9 +1,11 @@ -import { Component, Input, Output, EventEmitter } from "@angular/core"; +import { Component, Input, Output, EventEmitter, ViewChildren, QueryList } from "@angular/core"; import { ParamDefinition } from "jalhyd"; import { NgParameter, ParamRadioConfig } from "../../formulaire/ngparam"; import { FieldSet } from "../../formulaire/fieldset"; +import { ParamFieldLineComponent } from "../param-field-line/param-field-line.component"; +import { BaseComponent } from "../base/base.component"; @Component({ selector: "field-set", @@ -19,7 +21,7 @@ import { FieldSet } from "../../formulaire/fieldset"; }` ] }) -export class FieldSetComponent { +export class FieldSetComponent extends BaseComponent { /** * field set attribute */ @@ -30,6 +32,20 @@ export class FieldSetComponent { this._fieldSet = fs; } + @ViewChildren(ParamFieldLineComponent) + private _paramComponents: QueryList<ParamFieldLineComponent>; + + /** + * événément de changement de validité + */ + @Output() + private onValid = new EventEmitter(); + + /** + * flag de validité de la saisie + */ + private _isValid: boolean = false; + private get fields() { return this._fieldSet.fields; } @@ -85,11 +101,13 @@ export class FieldSetComponent { * cf. https://angular.io/guide/component-interaction#parent-listens-for-child-event */ private onRadioClick(info: string) { - // console.log("FieldSetComponent " + info); // on renvoie l'info au parent this.onRadio.emit(info); } + /** + * événément de changement d'état d'un radio + */ @Output() private onRadio = new EventEmitter<string>(); @@ -100,6 +118,48 @@ export class FieldSetComponent { this.onSelectChange.emit(val); // on transmet au parent } + public get isValid() { + return this._isValid; + } + + /** + * calcul de la validité de tous les ParamFieldLineComponent de la vue + */ + private updateValidity() { + if (this._paramComponents != undefined) + this._isValid = this._paramComponents.reduce( + // callback + ( + // accumulator (valeur précédente du résultat) + acc, + // currentValue (élément courant dans le tableau) + param, + // currentIndex (indice courant dans le tableau) + currIndex, + // array (tableau parcouru) + array + ) => { + return acc && param.isValid; + } + // valeur initiale + , true); + } + + protected afterFirstViewChecked() { + this.updateValidity(); + } + + /** + * réception d'un événement de validité de ParamFieldLineComponent + */ + private onParamLineValid(event: boolean) { + this.updateValidity(); + this.onValid.emit(); + } + + /** + * événément de changement d'état d'un select + */ @Output() private onSelectChange = new EventEmitter<string>(); } diff --git a/src/app/components/generic-calculator/calculator.component.html b/src/app/components/generic-calculator/calculator.component.html index a1f621c7e..70d12c9e8 100644 --- a/src/app/components/generic-calculator/calculator.component.html +++ b/src/app/components/generic-calculator/calculator.component.html @@ -16,13 +16,14 @@ <div class="container-fluid"> <!-- chapitres --> <field-set *ngFor="let fs of fieldSets" [style.display]="getFieldsetStyleDisplay(fs.id)" [fieldSet]=fs (onRadio)=onRadioClick($event) - (onSelectChange)=onSelectChanged($event)></field-set> + (onSelectChange)=onSelectChanged($event) (onValid)=OnFieldsetValid()></field-set> </div> <!-- bouton calculer --> <div class="row"> <div class="col-12 text-center"> - <button type="button" class="button_compute" name="Calculer" (click)="doCompute()">{{uitextCalculer}}</button> + <button type="button" [ngClass]="(isCalculateDisabled) ? 'button_compute_err' : 'button_compute_ok'" name="Calculer" (click)="doCompute()" + [disabled]="isCalculateDisabled">{{uitextCalculer}}</button> <p></p> <p></p> </div> diff --git a/src/app/components/generic-calculator/calculator.component.ts b/src/app/components/generic-calculator/calculator.component.ts index ef8d98b05..d3b4ea580 100644 --- a/src/app/components/generic-calculator/calculator.component.ts +++ b/src/app/components/generic-calculator/calculator.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, DoCheck, OnDestroy, ViewChild } from "@angular/core"; +import { Component, OnInit, DoCheck, OnDestroy, ViewChild, ViewChildren, QueryList } from "@angular/core"; import { ActivatedRoute } from '@angular/router'; import { FormulaireService } from "../../services/formulaire/formulaire.service"; @@ -9,24 +9,38 @@ import { CalculatorType } from "../../formulaire/formulaire-definition"; import { CalculatorResultsComponent } from "../../components/calculator-results/calculator-results.component"; import { Observer } from "../../services/observer"; import { Subscription } from "rxjs/Subscription"; +import { FieldSetComponent } from "../field-set/field-set.component"; +import { BaseComponent } from "../base/base.component"; @Component({ selector: 'hydrocalc', templateUrl: "./calculator.component.html", styles: [` - .button_compute { + .button_compute_ok { height: 3em; width: 30%; } + .button_compute_err { + height: 3em; + width: 30%; + color: red + } + #close-button { font-size: 1.5em; } ` ] }) -export class GenericCalculatorComponent implements OnInit, DoCheck, OnDestroy, Observer { +export class GenericCalculatorComponent extends BaseComponent implements OnInit, DoCheck, OnDestroy, Observer { + /** + * liste des FieldSetComponent + */ + @ViewChildren(FieldSetComponent) + private _fieldsetComponents: QueryList<FieldSetComponent>; + /** * composant d'affichage des résultats */ @@ -45,9 +59,23 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, OnDestroy, O */ private _computeClicked: boolean; + /** + * flag de validité gloable de la saisie + * NB : la validité du bouton "calculer" dépend de la validité de la saisie dans l'UI et de celle indiquée par le formulaire. + * La validité de l'UI comprend la forme (pas de chaîne alpha dans les champs numériques, etc..). + * La validité formulaire comprend le domaine de définition des valeurs saisies. + */ + private _isUIValid: boolean = false; + + /** + * flag disabled du bouton "calculer" + */ + private isCalculateDisabled: boolean = true; + constructor(private intlService: InternationalisationService, private formulaireService: FormulaireService, private route: ActivatedRoute) { + super(); } private get fieldSets(): FieldSet[] { @@ -125,13 +153,8 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, OnDestroy, O } ngDoCheck() { - // let q = this.getParamFromSymbol("Q"); // A VIRER !!!! - // if (q != undefined) { - // q.radioState = ParamRadioConfig.VAR; - // q.minValue = 1.5; - // q.maxValue = 6; - // q.stepValue = 0.3; - // } + if (this._formulaire != undefined) + this.isCalculateDisabled = !(this._formulaire.isValid && this._isUIValid); } ngOnDestroy() { @@ -244,4 +267,41 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, OnDestroy, O this._formulaire.updateNodeType(); this._formulaire.applyDependencies(); } + + /** + * appelé après le 1er affichage du composant + */ + protected afterFirstViewChecked() { + this.updateUIValidity(); + } + + /** + * calcul de la validité globale de la vue + */ + private updateUIValidity() { + if (this._fieldsetComponents != undefined) + this._isUIValid = this._fieldsetComponents.reduce( + // callback + ( + // accumulator (valeur précédente du résultat) + acc, + // currentValue (élément courant dans le tableau) + fieldset, + // currentIndex (indice courant dans le tableau) + currIndex, + // array (tableau parcouru) + array + ) => { + return acc && fieldset.isValid; + } + // valeur initiale + , true); + } + + /** + * réception d'un événement de validité d'un FieldSetComponent + */ + private OnFieldsetValid() { + this.updateUIValidity(); + } } diff --git a/src/app/components/param-field-line/param-field-line.component.html b/src/app/components/param-field-line/param-field-line.component.html index e99500c62..815da06bd 100644 --- a/src/app/components/param-field-line/param-field-line.component.html +++ b/src/app/components/param-field-line/param-field-line.component.html @@ -1,7 +1,7 @@ <div class="row"> <!-- input de saisie de la valeur --> <div class="col-12 col-sm-9 pt-3"> - <ngparam-input [inputDisabled]="isInputDisabled" [param]="_param" [title]="title"></ngparam-input> + <ngparam-input [inputDisabled]="isInputDisabled" [param]="_param" [title]="title" (onChange)="onInputChange($event)"></ngparam-input> </div> <div class="btn-group col" role="group"> @@ -30,4 +30,4 @@ </div> </div> -<param-values *ngIf="isRadioVarChecked" [param]="_param"></param-values> \ No newline at end of file +<param-values *ngIf="isRadioVarChecked" [param]="_param" (onValid)=onParamValuesValid($event)></param-values> \ No newline at end of file diff --git a/src/app/components/param-field-line/param-field-line.component.ts b/src/app/components/param-field-line/param-field-line.component.ts index 6e7a402ee..3a9c63c26 100644 --- a/src/app/components/param-field-line/param-field-line.component.ts +++ b/src/app/components/param-field-line/param-field-line.component.ts @@ -1,8 +1,8 @@ -import { Component, ViewChild, Input, Output, EventEmitter } from "@angular/core"; +import { Component, ViewChild, Input, Output, EventEmitter, AfterViewChecked } from "@angular/core"; import { InternationalisationService } from "../../services/internationalisation/internationalisation.service"; import { NgParameter, ParamRadioConfig } from "../../formulaire/ngparam"; -import { GenericInputComponent } from "../generic-input/generic-input.component"; +import { NgParamInputComponent } from "../ngparam-input/ngparam-input.component"; @Component({ selector: "param-field-line", @@ -24,11 +24,28 @@ import { GenericInputComponent } from "../generic-input/generic-input.component" }` ] }) -export class ParamFieldLineComponent { +export class ParamFieldLineComponent implements AfterViewChecked { @Input("param") private _param: NgParameter; + @ViewChild(NgParamInputComponent) + private _ngParamInputComponent: NgParamInputComponent; + + @Output() + private onValid: EventEmitter<void>; + + /** + * true si la valeur saisie est valide + */ + private _isInputValid: boolean = false; + + /** + * true si le min-max/liste est valide + */ + private _isRangeValid: boolean = true; + constructor(private intlService: InternationalisationService) { + this.onValid = new EventEmitter(); } private get title(): string { @@ -103,19 +120,14 @@ export class ParamFieldLineComponent { * calcule l'état du radio "paramètre fixé" */ private get radioFixCheck(): string { - if (this._param.radioState == ParamRadioConfig.FIX) - return "checked"; - return undefined; + return this.isRadioFixChecked ? "checked" : undefined; } /** * calcule l'état du radio "paramètre à varier" */ private get radioVarCheck(): string { - // console.log(this._param.symbol + " " + ParamRadioConfig[this._param.radioState]); - if (this._param.radioState == ParamRadioConfig.VAR) - return "checked"; - return undefined; + return this.isRadioVarChecked ? "checked" : undefined; } /** @@ -128,8 +140,15 @@ export class ParamFieldLineComponent { } /** - * retourne l'état du radio "paramètre à varier" sous forme booléenne - */ + * retourne l'état du radio "paramètre fixé" sous forme booléenne + */ + private get isRadioFixChecked(): boolean { + return this._param.radioState == ParamRadioConfig.FIX; + } + + /** + * retourne l'état du radio "paramètre à varier" sous forme booléenne + */ private get isRadioVarChecked(): boolean { return this._param.radioState == ParamRadioConfig.VAR; } @@ -145,6 +164,9 @@ export class ParamFieldLineComponent { private onRadioClick(symbol: string, option: string) { this.onRadio.emit(symbol + "_" + option); + + // MAJ validité + this.emitValidity(); } /** @@ -190,4 +212,49 @@ export class ParamFieldLineComponent { return this.radioCalCheck == undefined ? this.offClass : this.onClass; return ""; } + + /** + * validité des saisies du composant + */ + public get isValid(): boolean { + switch (this._param.radioState) { + case ParamRadioConfig.FIX: + return this._isInputValid + + case ParamRadioConfig.VAR: + return this._isRangeValid; + + default: + return true; + } + } + + /** + * émission d'un événement de validité + */ + private emitValidity() { + this.onValid.emit() + } + + /** + * réception d'un événement de NgParamInputComponent + */ + private onInputChange(event: any) { + if (event.action == "valid") { + this._isInputValid = event.value; + this.emitValidity(); + } + } + + /** + * réception d'un événement de validité de ParamValuesComponent + */ + private onParamValuesValid(event: boolean) { + this._isRangeValid = event; + this.emitValidity() + } + + public ngAfterViewChecked() { + this._ngParamInputComponent.showError = this.isRadioFixChecked; + } } diff --git a/src/app/components/param-values/ngparam-max.component.ts b/src/app/components/param-values/ngparam-max.component.ts index d1725c478..9fd292bd8 100644 --- a/src/app/components/param-values/ngparam-max.component.ts +++ b/src/app/components/param-values/ngparam-max.component.ts @@ -1,48 +1,37 @@ -import { Component, Output, EventEmitter } from "@angular/core"; +import { Component, Input } from "@angular/core"; -import { NumericalString, Pair } from "jalhyd"; +import { NumericalString } from "jalhyd"; import { GenericInputComponent } from "../generic-input/generic-input.component"; import { InternationalisationService } from "../../services/internationalisation/internationalisation.service"; +import { NgParameter } from "../../formulaire/ngparam"; @Component({ selector: "ngparam-max", templateUrl: "../generic-input/generic-input.component.html" }) export class NgParamMaxComponent extends GenericInputComponent { - /** - * valeur actuelle du maximum - */ - private _currentValue: number; - - /** - * reférence (valeur maxi pour le maximum) - */ - private _refValue: Pair; + @Input("param") + private _param: NgParameter; constructor(private intlService: InternationalisationService) { super(); } - public set refValue(v: Pair) { - this._refValue = v; - this.validateModel(); - } - protected getModelValue(): any { - return this._currentValue; + return this._param.maxValue; } protected setModelValue(v: any) { - this._currentValue = v; + this._param.maxValue = v; } protected validateModelValue(v: any): { isValid: boolean, message: string } { let msg = undefined; let valid = false; - if (!this._refValue.intervalHasValue(v)) - msg = "La valeur n'est pas dans " + this._refValue.toString(); + if (!this._param.checkMax(v)) + msg = "La valeur n'est pas dans ]" + this._param.minValue + " , " + this._param.domain.maxValue + "]"; else valid = true; diff --git a/src/app/components/param-values/ngparam-min.component.ts b/src/app/components/param-values/ngparam-min.component.ts index 8ee81a79c..a135468af 100644 --- a/src/app/components/param-values/ngparam-min.component.ts +++ b/src/app/components/param-values/ngparam-min.component.ts @@ -1,50 +1,37 @@ -import { Component, Output, EventEmitter } from "@angular/core"; +import { Component, Input } from "@angular/core"; -import { NumericalString, Pair } from "jalhyd"; +import { NumericalString } from "jalhyd"; import { GenericInputComponent } from "../generic-input/generic-input.component"; import { InternationalisationService } from "../../services/internationalisation/internationalisation.service"; +import { NgParameter } from "../../formulaire/ngparam"; @Component({ selector: "ngparam-min", templateUrl: "../generic-input/generic-input.component.html" }) export class NgParamMinComponent extends GenericInputComponent { - /** - * valeur actuelle du minimum - */0.49999999995 - private _currentValue: number; - - /** - * reférence (valeur mini pour le minimum) - */ - private _refValue: Pair; - - public isInit: boolean; + @Input("param") + private _param: NgParameter; constructor(private intlService: InternationalisationService) { super(); } - public set refValue(v: Pair) { - this._refValue = v; - this.validateModel(); - } - protected getModelValue(): any { - return this._currentValue; + return this._param.minValue; } protected setModelValue(v: any) { - this._currentValue = v; + this._param.minValue = v; } protected validateModelValue(v: any): { isValid: boolean, message: string } { let msg = undefined; let valid = false; - if (!this._refValue.intervalHasValue(v)) - msg = "La valeur n'est pas dans " + this._refValue.toString(); + if (!this._param.checkMin(v)) + msg = "La valeur n'est pas dans [" + this._param.domain.minValue + " , " + this._param.maxValue + "["; else valid = true; diff --git a/src/app/components/param-values/ngparam-step.component.ts b/src/app/components/param-values/ngparam-step.component.ts index 2f630c917..1d25366bf 100644 --- a/src/app/components/param-values/ngparam-step.component.ts +++ b/src/app/components/param-values/ngparam-step.component.ts @@ -1,53 +1,47 @@ -import { Component, Output, EventEmitter } from "@angular/core"; +import { Component, Input } from "@angular/core"; -import { NumericalString, Pair } from "jalhyd"; +import { NumericalString } from "jalhyd"; import { GenericInputComponent } from "../generic-input/generic-input.component"; import { InternationalisationService } from "../../services/internationalisation/internationalisation.service"; +import { NgParameter } from "../../formulaire/ngparam"; @Component({ selector: "ngparam-step", templateUrl: "../generic-input/generic-input.component.html" }) export class NgParamStepComponent extends GenericInputComponent { - /** - * valeur actuelle du pas - */ - private _currentValue: number; - - /** - * reférence (valeur maxi pour le pas) - */ - private _refValue: Pair; + @Input("param") + private _param: NgParameter; constructor(private intlService: InternationalisationService) { super(); } - public set refValue(v: Pair) { - this._refValue = v; - this.validateModel(); - } - protected getModelValue(): any { - return this._currentValue; + return this._param.stepValue; } protected setModelValue(v: any) { - this._currentValue = v; + this._param.stepValue = v; } protected validateModelValue(v: any): { isValid: boolean, message: string } { let msg = undefined; let valid = false; - if (!this._refValue.intervalHasValue(v)) - msg = "La valeur n'est pas dans " + this._refValue.toString(); - else { - valid = v > 0; - if (!valid) - msg = "La valeur ne peut pas être <= 0"; + if (this._param.isMinMaxValid) { + if (!this._param.checkStep(v)) { + msg = "La valeur n'est pas dans " + this._param.stepRefValue.toString(); + } + else { + valid = v > 0; + if (!valid) + msg = "La valeur ne peut pas être <= 0"; + } } + else + msg = "Veuillez corriger le min/max"; return { isValid: valid, message: msg }; } diff --git a/src/app/components/param-values/param-values.component.html b/src/app/components/param-values/param-values.component.html index e3adf9e93..b576cf138 100644 --- a/src/app/components/param-values/param-values.component.html +++ b/src/app/components/param-values/param-values.component.html @@ -1,7 +1,7 @@ <div class="row"> <div class="btn-group col-12 col-sm-3" dropdown (click)="onSelectValueMode($event)"> <button dropdownToggle class="btn btn-primary dropdown-toggle waves-light my-1" type="button" mdbRippleRadius> - {{currentLabel}} + {{currentModeSelectLabel}} </button> <div class="dropdown-menu"> <a class="dropdown-item" *ngFor="let e of _valueModes" [value]=e.value>{{e.label}}</a> @@ -9,16 +9,16 @@ </div> <div *ngIf="!isList" class="col-12 col-sm-3"> - <ngparam-min [title]="uitextValeurMini" (onChange)="onMinChanged($event)"></ngparam-min> + <ngparam-min [title]="uitextValeurMini" [param]=_param (onChange)="onMinChanged($event)"></ngparam-min> </div> <div *ngIf="!isList" class="col-12 col-sm-3"> - <ngparam-max [title]="uitextValeurMaxi" (onChange)="onMaxChanged($event)"></ngparam-max> + <ngparam-max [title]="uitextValeurMaxi" [param]=_param (onChange)="onMaxChanged($event)"></ngparam-max> </div> <div *ngIf="!isList" class="col-12 col-sm-3"> - <ngparam-step [title]="uitextPasVariation" (onChange)="onStepChanged($event)"></ngparam-step> + <ngparam-step [title]="uitextPasVariation" [param]=_param (onChange)="onStepChanged($event)"></ngparam-step> </div> <div *ngIf="isList" class="col-12 col-sm-6"> - <value-list title="valeurs séparées par ';'" (onChange)="onListChanged($event)"></value-list> + <value-list title="valeurs séparées par ';'" [param]=_param (onChange)="onListChanged($event)"></value-list> </div> </div> \ No newline at end of file diff --git a/src/app/components/param-values/param-values.component.ts b/src/app/components/param-values/param-values.component.ts index feb09c709..7a579e5e2 100644 --- a/src/app/components/param-values/param-values.component.ts +++ b/src/app/components/param-values/param-values.component.ts @@ -1,99 +1,12 @@ -import { Component, Input, Output, EventEmitter, ViewChild, DoCheck } from "@angular/core"; - -import { ParamDomainValue, Pair } from "jalhyd"; +import { Component, Input, Output, EventEmitter, ViewChild, AfterViewChecked } from "@angular/core"; import { InternationalisationService } from "../../services/internationalisation/internationalisation.service"; import { NgParameter, ParamValueMode } from "../../formulaire/ngparam"; -import { GenericInputComponent } from "../generic-input/generic-input.component"; import { NgParamMinComponent } from "./ngparam-min.component"; import { NgParamMaxComponent } from "./ngparam-max.component"; import { NgParamStepComponent } from "./ngparam-step.component"; - - -@Component({ - selector: "value-list", - templateUrl: "../generic-input/generic-input.component.html" -}) -export class ValueListComponent extends GenericInputComponent { - public _list: number[]; - - public isInit; - - constructor(private intlService: InternationalisationService) { - super(); - this._list = []; - } - - protected getModelValue(): any { - return this._list; - } - - protected setModelValue(l: any) { - if (typeof (l) == "number") { - this._list = []; - this._list.push(l); - } - else - this._list = l; - } - - protected validateModelValue(v: any): { isValid: boolean, message: string } { - let msg = undefined; - let valid = false; - - if (v instanceof Array) { - valid = true; - for (let e of v) { - valid = valid && (typeof (e) == "number") - if (!valid) { - msg = "La valeur n'est pas une liste de nombres" - break; - } - } - } - - return { isValid: valid, message: msg }; - } - - protected modelToUI(v: any): string { - let res = ""; - for (let e of v) { - if (res != "") - res += ";"; - res += String(e); - } - return res; - } - - protected validateUIValue(ui: string): { isValid: boolean, message: string } { - let valid: boolean = false; - let msg: string = undefined; - - let tmp: string[] = ui.split(";"); - let res = true; - for (let v of tmp) { - let isnum = v != "" && (+v == +v); - res = res && isnum; - if (!res) - break; - } - - if (!res) - msg = "Veuillez entrer une liste de nombres"; - else - valid = true; - - return { isValid: valid, message: msg }; - } - - protected uiToModel(ui: string) { - let tmp: string[] = ui.split(";"); - let res = []; - for (let v of tmp) - res.push(+v); - return res; - } -} +import { BaseComponent } from "../base/base.component"; +import { ValueListComponent } from "./value-list.component"; @Component({ selector: "param-values", @@ -115,16 +28,41 @@ export class ValueListComponent extends GenericInputComponent { }` ] }) -export class ParamValuesComponent implements DoCheck { +export class ParamValuesComponent extends BaseComponent implements AfterViewChecked { @Input("param") private _param: NgParameter; + private _valueModes = []; + /** - * true si liste de valeur, false si min/max/pas + * true quand les champs du composant et de ses enfants sont initialisés */ - private _list: boolean; + private _initCompleted = false; - private _valueModes = []; + /** + * true si la valeur min est valide + */ + private _validMin: boolean = false; + + /** + * true si la valeur max est valide + */ + private _validMax: boolean = false; + + /** + * true si la valeur du pas est valide + */ + private _validStep: boolean = false; + + /** + * true si la liste de valeurs est valide + */ + private _validList: boolean = false; + + /** + * flag signalant qu'on a sélectionné le mode "liste" et qu'il faut initialiser le composant ValueListComponent + */ + private _doInitList: boolean = false; /** * composant de saisie du minimum @@ -150,102 +88,171 @@ export class ParamValuesComponent implements DoCheck { @ViewChild(ValueListComponent) private _listComponent: ValueListComponent; + @Output() + private onValid: EventEmitter<boolean>; + constructor(private intlService: InternationalisationService) { + super(); this._valueModes.push({ "value": ParamValueMode.MINMAX, "label": "Min/max" }); this._valueModes.push({ "value": ParamValueMode.LISTE, "label": "Liste" }); + this.onValid = new EventEmitter(); } + /** + * init des champs min/max/pas + */ private initMinMaxStep() { - // valeur pour min (celle déjà définie ou celle déduite du domaine de définition) - let min: number = this._param.minValue; - let ok = min != undefined - if (ok) { - try { - // on la vérifie - this._param.checkValue(min); - ok = true; - } - catch (e) { - ok = false; - } - } - if (!ok) - min = this._param.getValue() / 2; - - // valeur pour max (celle déjà définie ou celle déduite du domaine de définition) - let max: number = this._param.maxValue; - ok = max != undefined - if (ok) { - try { - // on la vérifie - this._param.checkValue(max); - ok = true; - } - catch (e) { - ok = false; - } - } - if (!ok) - max = this._param.getValue() * 2; + if (this.isMinMax) { + // valeur pour min : celle déjà définie ou celle déduite de la valeur saisie + let min: number = this._param.minValue; + if (min == undefined) + min = this._param.getValue() / 2; - this._minComponent.refValue = new Pair(this._param.domain.minValue, max); - this._minComponent.model = min; + // valeur pour max : celle déjà définie ou celle déduite de la valeur saisie + let max: number = this._param.maxValue; + if (max == undefined) + max = this._param.getValue() * 2; - this._maxComponent.refValue = new Pair(min, this._param.domain.maxValue); - this._maxComponent.model = max; + this._minComponent.model = min; - this.updateStepComponentRef(); + this._maxComponent.model = max; - this._stepComponent.model = (this._maxComponent.model - this._minComponent.model) / 20; + // valeur du pas + let step = this._param.stepValue; + if (step == undefined) + step = (this._maxComponent.model - this._minComponent.model) / 20; + this._stepComponent.model = step; + + this.validateAll(); + + this._validMin = this._minComponent.isValid; + this._validMax = this._maxComponent.isValid; + this._validStep = this._stepComponent.isValid; + this.emitValidity(); + + this._initCompleted = true; + } } - private onMinChanged(val: string) { - this.updateStepComponentRef(); + /** + * initialisation de la liste de valeurs avec celle du paramètre géré + */ + private initList() { + if (this._doInitList && this._listComponent != undefined) { + this._doInitList = false; + let l = this._param.valueList; + if (l == undefined) { + if (this._param.isDefined) + l = [this._param.getValue()]; + else + l = []; + } + this._listComponent.model = l; + } + } - this._maxComponent.refValue = new Pair(this._minComponent.model, this._param.domain.maxValue); - if (this._minComponent.isValid) - this._param.minValue = this._minComponent.model; + /** + * revalidation de tous les composants enfants + */ + private validateAll() { + if (this._minComponent != undefined) + this._minComponent.validate(); + if (this._maxComponent != undefined) + this._maxComponent.validate(); + if (this._stepComponent != undefined) + this._stepComponent.validate(); + if (this._listComponent != undefined) + this._listComponent.validate(); } - private onMaxChanged(val: string) { - this.updateStepComponentRef(); + /** + * envoi d'un événement de validité + */ + private emitValidity() { + switch (this._param.valueMode) { + case ParamValueMode.LISTE: + this.onValid.emit(this._validList); + break; - this._minComponent.refValue = new Pair(this._param.domain.minValue, this._maxComponent.model); - if (this._maxComponent.isValid) - this._param.maxValue = this._maxComponent.model; + case ParamValueMode.MINMAX: + this.onValid.emit(this._validMin && this._validMax && this._validStep); + break; + } } - private onStepChanged(val: string) { - if (this._stepComponent.isValid) - this._param.stepValue = this._stepComponent.model; + /** + * réception d'un événement de NgParamMinComponent + */ + private onMinChanged(event: any) { + if (this._initCompleted) + switch (event.action) { + case "model": + this.validateAll(); + break; + + case "valid": + this._validMin = event.value; + this.emitValidity(); + break; + } } - private onListChanged(val: string) { - if (this._listComponent.isValid) - this._param.valueList = this._listComponent.model; + /** + * réception d'un événement de NgParamMaxComponent + */ + private onMaxChanged(event: any) { + if (this._initCompleted) + switch (event.action) { + case "model": + this.validateAll(); + break; + + case "valid": + this._validMax = event.value; + this.emitValidity(); + break; + } } - public ngDoCheck() { - // initialisation des champs min/max/step à l'apparition de ces contrôles - if (this._minComponent != undefined && !this._minComponent.isInit) { - this._minComponent.isInit = true; - this.initMinMaxStep(); - } + /** + * réception d'un événement de NgParamStepComponent + */ + private onStepChanged(event: any) { + if (this._initCompleted) + switch (event.action) { + case "model": + this.validateAll(); + break; - if (this._listComponent != undefined && !this._listComponent.isInit) { - this._listComponent.isInit = true; + case "valid": + this._validStep = event.value; + this.emitValidity(); + break; + } + } - this._listComponent.model = []; - if (this._param.isDefined) - this._listComponent.model.push(this._param.getValue()); - } + /** + * réception d'un événement de ValueListComponent + */ + private onListChanged(event: any) { + if (this._initCompleted) + switch (event.action) { + case "model": + this.validateAll(); + break; + + case "valid": + this._validList = event.value; + this.emitValidity(); + break; + } } /** - * met à jour la valeur de référence du composant gérant le pas de variation + * appelé au 1er affichage du composant */ - private updateStepComponentRef() { - this._stepComponent.refValue = new Pair(1e-9, this._maxComponent.model - this._minComponent.model); + protected afterFirstViewChecked() { + this.initMinMaxStep(); } private get uitextValeurMini() { @@ -260,18 +267,43 @@ export class ParamValuesComponent implements DoCheck { return this.intlService.localizeText("INFO_PARAMFIELD_PASVARIATION"); } + /** + * true si mode "liste de valeurs" + */ private get isList(): boolean { return this._param.valueMode == ParamValueMode.LISTE; } /** - * valeur courante affichée dans le select + * true si mode "min/max/pas" + */ + private get isMinMax(): boolean { + return this._param.valueMode == ParamValueMode.MINMAX; + } + + /** + * valeur courante affichée dans le select min-max/list */ - private get currentLabel(): string { + private get currentModeSelectLabel(): string { return ParamValueMode[this._param.valueMode]; } + /** + * réception d'un événement du menu "min/max/liste" + */ private onSelectValueMode(event: any) { - this._param.valueMode = event.target.value; + const next = event.target.value; + + // on a sélectionné "liste" ? + if (this._param.valueMode != ParamValueMode.LISTE && next == ParamValueMode.LISTE) + this._doInitList = true; + + this._param.valueMode = next; + this.validateAll(); + } + + ngAfterViewChecked() { + super.ngAfterViewChecked(); + this.initList(); } } diff --git a/src/app/components/param-values/value-list.component.ts b/src/app/components/param-values/value-list.component.ts new file mode 100644 index 000000000..e3f3adde6 --- /dev/null +++ b/src/app/components/param-values/value-list.component.ts @@ -0,0 +1,95 @@ +import { Component, Input } from "@angular/core"; + +import { GenericInputComponent } from "../generic-input/generic-input.component"; +import { InternationalisationService } from "../../services/internationalisation/internationalisation.service"; +import { NgParameter } from "../../formulaire/ngparam"; +import { Message } from "jalhyd"; + +@Component({ + selector: "value-list", + templateUrl: "../generic-input/generic-input.component.html" +}) +export class ValueListComponent extends GenericInputComponent { + @Input("param") + private _param: NgParameter; + + constructor(private intlService: InternationalisationService) { + super(); + } + + protected getModelValue(): any { + return this._param.valueList; + } + + protected setModelValue(l: any) { + if (typeof (l) == "number") { + this._param.valueList = []; + this._param.valueList.push(l); + } + else + this._param.valueList = l; + } + + protected validateModelValue(v: any): { isValid: boolean, message: string } { + let msg = undefined; + let valid = false; + + if (v instanceof Array) { + valid = true; + try { + this._param.checkList(v); + } + catch (ex) { + valid = false; + if (ex instanceof Message) + msg = this.intlService.localizeMessage(ex); + else + msg = "invalid value"; + } + } + else + msg = "Veuillez entrer une liste de nombres"; + + return { isValid: valid, message: msg }; + } + + protected modelToUI(v: any): string { + let res = ""; + if (v != undefined) + for (let e of v) { + if (res != "") + res += ";"; + res += String(e); + } + return res; + } + + protected validateUIValue(ui: string): { isValid: boolean, message: string } { + let valid: boolean = false; + let msg: string = undefined; + + let tmp: string[] = ui.split(";"); + let res = true; + for (let v of tmp) { + let isnum = v != "" && (+v == +v); + res = res && isnum; + if (!res) + break; + } + + if (!res) + msg = "Veuillez entrer une liste de nombres"; + else + valid = true; + + return { isValid: valid, message: msg }; + } + + protected uiToModel(ui: string) { + let tmp: string[] = ui.split(";"); + let res = []; + for (let v of tmp) + res.push(+v); + return res; + } +} -- GitLab