Skip to content
Snippets Groups Projects
Commit bd71a745 authored by mathias.chouet's avatar mathias.chouet
Browse files

Fix #107 Paramètres variables : amélioration de la saisie d'une liste de valeurs

parent ef142048
No related branches found
No related tags found
1 merge request!29Resolve "Remplacer mdbootstrap par angular-material"
......@@ -40,12 +40,15 @@
<form [formGroup]="valuesListForm">
<mat-form-field>
<textarea matInput matTextareaAutosize [placeholder]="uitextListeValeurs" formControlName="valuesList"
[value]="valuesList" (input)="valuesList = $event.target.value"></textarea>
[value]="valuesList"></textarea>
<!-- (input)="valuesList = $event.target.value" -->
<mat-error>
{{ uitextMustBeListOfNumbers }}
<!-- <div *ngIf="! vl.errors.required && vl.errors.jalhydModel">
{{ vl.errors.jalhydModel.message }}
</div> -->
<span *ngIf="valuesListForm.controls.valuesList.hasError('model')">
{{ valuesListForm.controls.valuesList.errors.model }}
</span>
<span *ngIf="! valuesListForm.controls.valuesList.hasError('model')">
{{ uitextMustBeListOfNumbers }}
</span>
</mat-error>
</mat-form-field>
......@@ -76,7 +79,17 @@
</div>
<div mat-dialog-actions>
<button mat-raised-button [mat-dialog-close]="true" cdkFocusInitial>
{{ uitextClose }}
</button>
<div *ngIf="isMinMax">
<button mat-raised-button [mat-dialog-close]="true" cdkFocusInitial>
{{ uitextClose }}
</button>
</div>
<div *ngIf="isListe">
<button mat-raised-button color="primary" [mat-dialog-close]="true" cdkFocusInitial>
{{ uitextCancel }}
</button>
<button mat-raised-button color="warn" (click)="onValidate()">
{{ uitextValidate }}
</button>
</div>
</div>
......@@ -17,5 +17,5 @@ mat-form-field {
}
.decimal-separator-and-file-container {
margin-top: 5px;
margin-top: 1em;
}
......@@ -38,12 +38,13 @@ export class DialogEditParamValuesComponent implements OnInit {
// an explicit ReactiveForm is required for file input component
this.valuesListForm = this.fb.group({
file: [""],
valuesList: [this.valuesList, [
Validators.required,
Validators.pattern(new RegExp(this.valuesListPattern))
valuesList: ["", [ // not initialized with valuesList because param mode is MIN/MAX at this time
Validators.required
// Validators.pattern(new RegExp(this.valuesListPattern)) // behaves weirdly
]]
});
// available options for select controls
this.valueModes = [
{
value: ParamValueMode.MINMAX,
......@@ -73,8 +74,8 @@ export class DialogEditParamValuesComponent implements OnInit {
public get valuesListPattern() {
// standard pattern for decimal separator "." : ^-?([0-9]*\.)?([0-9]+[Ee]-?)?[0-9]+$
const escapedDecimalSeparator = (this.decimalSeparator === "." ? "\\." : this.decimalSeparator);
const numberSubPattern = `^-?([0-9]*${escapedDecimalSeparator})?([0-9]+[Ee]-?)?[0-9]+$`;
const re = numberSubPattern + "(" + this.separatorPattern + numberSubPattern + ")*";
const numberSubPattern = `-?([0-9]*${escapedDecimalSeparator})?([0-9]+[Ee]-?)?[0-9]+`;
const re = `^${numberSubPattern}(${this.separatorPattern}${numberSubPattern})*$`;
return re;
}
......@@ -93,50 +94,6 @@ export class DialogEditParamValuesComponent implements OnInit {
this.param.valueMode = v;
}
public get uiTextModeSelection() {
return this.intlService.localizeText("INFO_PARAMFIELD_PARAMVARIER_MODE");
}
public get uitextValeurMini() {
return this.intlService.localizeText("INFO_PARAMFIELD_VALEURMINI");
}
public get uitextValeurMaxi() {
return this.intlService.localizeText("INFO_PARAMFIELD_VALEURMAXI");
}
public get uitextPasVariation() {
return this.intlService.localizeText("INFO_PARAMFIELD_PASVARIATION");
}
public get uitextClose() {
return this.intlService.localizeText("INFO_OPTION_CLOSE");
}
public get uitextEditParamVariableValues() {
return this.intlService.localizeText("INFO_PARAMFIELD_PARAMVARIER_TITLE");
}
public get uitextListeValeurs() {
return this.intlService.localizeText("INFO_PARAMFIELD_PARAMVARIER_VALUES_FORMAT");
}
public get uitextMustBeANumber(): string {
return this.intlService.localizeText("ERROR_PARAM_MUST_BE_A_NUMBER");
}
public get uitextMustBeListOfNumbers() {
return sprintf(this.intlService.localizeText("INFO_PARAMFIELD_PARAMVARIER_VALUES_FORMAT_ERROR"), this.separatorPattern);
}
public get uitextDecimalSeparator() {
return this.intlService.localizeText("INFO_PARAMFIELD_PARAMVARIER_SEPARATEUR_DECIMAL");
}
public get uitextImportFile() {
return this.intlService.localizeText("INFO_PARAMFIELD_PARAMVARIER_IMPORT_FICHIER");
}
public get isMinMax() {
return this.param.valueMode === ParamValueMode.MINMAX;
}
......@@ -149,13 +106,7 @@ export class DialogEditParamValuesComponent implements OnInit {
* renders model's numbers list as text values list (semicolon separated)
*/
public get valuesList() {
let baseValue;
if (this.param.valueMode === ParamValueMode.LISTE) {
// otherwise JalHyd model complains when reading this.param.valueList
baseValue = this.param.valueList;
}
const ret = (baseValue || []).join(";");
return ret;
return (this.param.valueList || []).join(";");
}
/**
......@@ -169,7 +120,7 @@ export class DialogEditParamValuesComponent implements OnInit {
if (e.length > 0) {
// ensure decimal separator is "." for Number()
if (this.decimalSeparator !== ".") {
const re = new RegExp(this.decimalSeparator, "g");
const re = new RegExp(this.decimalSeparator, "g"); // @TODO remove "g" ?
e = e.replace(re, ".");
}
vals.push(Number(e));
......@@ -178,11 +129,75 @@ export class DialogEditParamValuesComponent implements OnInit {
this.param.valueList = vals;
}
public onValidate() {
const status = this.validateValuesListString(this.valuesListForm.controls.valuesList.value);
if (status.ok) {
this.valuesListForm.controls.valuesList.setErrors(null);
this.valuesList = this.valuesListForm.controls.valuesList.value;
this.dialogRef.close();
} else {
this.valuesListForm.controls.valuesList.setErrors({ "model": status.message });
}
}
/**
* Returns { ok: true } if every element of list is a valid Number, { ok: false, message: "reason" } otherwise
* @param list a string containing a list of numbers separated by this.separatorPattern
*/
private validateValuesListString(list: string) {
let message: string;
// 1. validate against general pattern. Should not be necessary since "validate" button is disabled
// if pattern is not matched, BUT, HTML5 "pattern" behaves weirdly and accepts multiple decimal
// separators, like "3.3.4" :/
let ok = new RegExp(this.valuesListPattern).test(list);
if (ok) {
// 2. validate each value
const separatorRE = new RegExp(this.separatorPattern);
const parts = list.trim().split(separatorRE);
for (let i = 0; i < parts.length && ok; i++) {
let e = parts[i];
if (e.length > 0) { // should always be true as separator might be several characters long
// ensure decimal separator is "." for Number()
if (this.decimalSeparator !== ".") {
const re = new RegExp(this.decimalSeparator, "g"); // @TODO remove "g" ?
e = e.replace(re, ".");
}
// 2.1 check it is a valid Number
const n = (Number(e));
// 2.2 validate against model
let modelIsHappy = true;
try {
this.param.checkValue(n);
} catch (e) {
modelIsHappy = false;
message = sprintf(this.intlService.localizeText("ERROR_INVALID_AT_POSITION"), i + 1)
+ " " + this.intlService.localizeMessage(e);
}
// synthesis
ok = (
ok
&& !isNaN(n)
&& isFinite(n)
&& modelIsHappy
);
}
}
} else {
message = this.uitextMustBeListOfNumbers;
}
return { ok, message };
}
public onFileSelected(event: any) {
if (event.target.files && event.target.files.length) {
const fr = new FileReader();
fr.onload = () => {
this.valuesList = String(fr.result);
this.valuesListForm.controls.valuesList.setErrors(null);
// this.valuesList = String(fr.result);
this.valuesListForm.controls.valuesList.setValue(String(fr.result));
};
fr.onerror = () => {
fr.abort();
......@@ -196,10 +211,6 @@ export class DialogEditParamValuesComponent implements OnInit {
this.initVariableValues();
}
public ngOnInit() {
this.initVariableValues();
}
private initVariableValues() {
// init min / max / step
if (this.isMinMax) {
......@@ -223,10 +234,67 @@ export class DialogEditParamValuesComponent implements OnInit {
} else {
this.param.valueList = [];
}
// set form control initial value
this.valuesListForm.controls.valuesList.setValue(this.valuesList);
}
}
}
public get uiTextModeSelection() {
return this.intlService.localizeText("INFO_PARAMFIELD_PARAMVARIER_MODE");
}
public get uitextValeurMini() {
return this.intlService.localizeText("INFO_PARAMFIELD_VALEURMINI");
}
public get uitextValeurMaxi() {
return this.intlService.localizeText("INFO_PARAMFIELD_VALEURMAXI");
}
public get uitextPasVariation() {
return this.intlService.localizeText("INFO_PARAMFIELD_PASVARIATION");
}
public get uitextClose() {
return this.intlService.localizeText("INFO_OPTION_CLOSE");
}
public get uitextCancel() {
return this.intlService.localizeText("INFO_OPTION_CANCEL");
}
public get uitextValidate() {
return this.intlService.localizeText("INFO_OPTION_VALIDATE");
}
public get uitextEditParamVariableValues() {
return this.intlService.localizeText("INFO_PARAMFIELD_PARAMVARIER_TITLE");
}
public get uitextListeValeurs() {
return this.intlService.localizeText("INFO_PARAMFIELD_PARAMVARIER_VALUES_FORMAT");
}
public get uitextMustBeANumber(): string {
return this.intlService.localizeText("ERROR_PARAM_MUST_BE_A_NUMBER");
}
public get uitextMustBeListOfNumbers() {
return sprintf(this.intlService.localizeText("INFO_PARAMFIELD_PARAMVARIER_VALUES_FORMAT_ERROR"), this.separatorPattern);
}
public get uitextDecimalSeparator() {
return this.intlService.localizeText("INFO_PARAMFIELD_PARAMVARIER_SEPARATEUR_DECIMAL");
}
public get uitextImportFile() {
return this.intlService.localizeText("INFO_PARAMFIELD_PARAMVARIER_IMPORT_FICHIER");
}
public ngOnInit() {
this.initVariableValues();
}
/* protected validateModelValue(v: any): { isValid: boolean, message: string } {
let msg: string;
......
......@@ -68,6 +68,7 @@ export class ParamValuesComponent implements AfterViewInit {
this.editValuesDialog.open(
DialogEditParamValuesComponent,
{
disableClose: true,
data: {
param: this.param
}
......
......@@ -11,6 +11,6 @@ export class ImmediateErrorStateMatcher implements ErrorStateMatcher {
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
const isSubmitted = form && form.submitted;
return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted));
return !!(control && control.invalid /* && (control.dirty || control.touched || isSubmitted) */ );
}
}
......@@ -8,6 +8,7 @@
"ERROR_DICHO_NULL_STEP": "Dichotomy (initial interval search): invalid null step",
"ERROR_INTERVAL_OUTSIDE": "Interval: value %value% is outside of %interval",
"ERROR_INTERVAL_UNDEF": "Interval: invalid 'undefined' value",
"ERROR_INVALID_AT_POSITION": "Position %s:",
"ERROR_LANG_UNSUPPORTED": "internationalisation: unsupported '%locale%' locale",
"ERROR_NEWTON_DERIVEE_NULLE": "Null function derivative in Newton computation",
"ERROR_PARAM_NULL": "Parameter value must not be NULL",
......@@ -129,6 +130,7 @@
"INFO_OPTION_CLOSE": "Close",
"INFO_OPTION_LOAD": "Load",
"INFO_OPTION_SAVE": "Save",
"INFO_OPTION_VALIDATE": "Validate",
"INFO_OPTION_ALL": "All",
"INFO_OPTION_ALL_F": "All",
"INFO_OPTION_NONE": "None",
......
......@@ -8,6 +8,7 @@
"ERROR_DICHO_NULL_STEP": "Dichotomie&nbsp;: le pas pour la recherche de l'intervalle de départ ne devrait pas être nul",
"ERROR_INTERVAL_OUTSIDE": "Interval&nbsp;: la valeur %value% est hors de l'intervalle %interval",
"ERROR_INTERVAL_UNDEF": "Interval&nbsp;: valeur 'undefined' incorrecte",
"ERROR_INVALID_AT_POSITION": "Position %s :",
"ERROR_LANG_UNSUPPORTED": "Internationalisation&nbsp;: locale '%locale%' non prise en charge",
"ERROR_NEWTON_DERIVEE_NULLE": "Dérivée nulle dans un calcul par la méthode de Newton",
"ERROR_PARAM_NULL": "La valeur du paramètre ne peut pas être NULL",
......@@ -129,6 +130,7 @@
"INFO_OPTION_CLOSE": "Fermer",
"INFO_OPTION_LOAD": "Charger",
"INFO_OPTION_SAVE": "Enregistrer",
"INFO_OPTION_VALIDATE": "Valider",
"INFO_OPTION_ALL": "Tous",
"INFO_OPTION_ALL_F": "Toutes",
"INFO_OPTION_NONE": "Aucun",
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment