An error occurred while loading the file. Please try again.
-
Harold Boissenin authorede3cd5ac0
import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material";
import { Inject, Component, OnInit } from "@angular/core";
import { FormBuilder, FormGroup, Validators } from "@angular/forms";
import { I18nService } from "../../services/internationalisation/internationalisation.service";
import { NgParameter } from "../../formulaire/ngparam";
import { ParamValueMode } from "jalhyd";
import { sprintf } from "sprintf-js";
import { ApplicationSetupService } from "../../services/app-setup/app-setup.service";
@Component({
selector: "dialog-edit-param-values",
templateUrl: "dialog-edit-param-values.component.html",
styleUrls: ["dialog-edit-param-values.component.scss"]
})
export class DialogEditParamValuesComponent implements OnInit {
/** the related parameter to change the "variable" value of */
public param: NgParameter;
/** available value modes (min / max, list) */
public valueModes: { value: ParamValueMode; label: string; }[];
/** available decimal separators */
public decimalSeparators: { label: string; value: string; }[];
/** current decimal separator */
public decimalSeparator: string;
public valuesListForm: FormGroup;
/** when true, shows the values chart instead of the edit form */
public viewChart = false;
// chart config
public chartData = {};
public chartOptions;
constructor(
public dialogRef: MatDialogRef<DialogEditParamValuesComponent>,
private intlService: I18nService,
private appSetupService: ApplicationSetupService,
private fb: FormBuilder,
@Inject(MAT_DIALOG_DATA) public data: any
) {
this.param = data.param;
// an explicit ReactiveForm is required for file input component
const initialValue = (this.param.valueMode === ParamValueMode.LISTE ? this.valuesList : "");
this.valuesListForm = this.fb.group({
file: [""],
valuesList: [ initialValue,
[
Validators.required
// Validators.pattern(new RegExp(this.valuesListPattern)) // behaves weirdly
]
]
});
// available options for select controls
this.valueModes = [
{
value: ParamValueMode.MINMAX,
label: this.intlService.localizeText("INFO_PARAMMODE_MINMAX")
},
{
value: ParamValueMode.LISTE,
label: this.intlService.localizeText("INFO_PARAMMODE_LIST")
}
];
this.decimalSeparators = [
{
label: this.intlService.localizeText("INFO_PARAMFIELD_PARAMVARIER_SEPARATEUR_POINT"),
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
value: "."
},
{
label: this.intlService.localizeText("INFO_PARAMFIELD_PARAMVARIER_SEPARATEUR_VIRGULE"),
value: ","
}
];
this.decimalSeparator = this.decimalSeparators[0].value;
// chart configuration
const nDigits = this.appSetupService.displayDigits;
this.chartOptions = {
responsive: true,
maintainAspectRatio: true,
animation: {
duration: 0
},
legend: {
display: false
},
scales: {
xAxes: [{
type: "linear",
position: "bottom",
ticks: {
precision: nDigits
}
}],
yAxes: [{
type: "linear",
position: "left",
ticks: {
precision: nDigits
}
}]
},
tooltips: {
callbacks: {
label: function(tooltipItem) {
return Number(tooltipItem.yLabel).toFixed(nDigits);
}
}
}
};
}
// proxy to model values
public get minValue() {
return this.param.minValue;
}
public set minValue(v) {
this.param.setMinValue(this, v);
}
public get maxValue() {
return this.param.maxValue;
}
public set maxValue(v) {
this.param.setMaxValue(this, v);
}
public get stepValue() {
return this.param.stepValue;
}
public set stepValue(v) {
this.param.setStepValue(this, v);
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
}
/**
* regular expression pattern for values list validation (depends on decimal separator)
*/
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})*$`;
return re;
}
/**
* accepted separator: everything but [numbers, E, +, -, decimal separator], any length
*/
public get separatorPattern() {
return "[^0-9-+Ee" + this.decimalSeparator + "]+";
}
public get selectedValueMode() {
return this.param.valueMode;
}
public set selectedValueMode(v) {
this.param.valueMode = v;
}
public get isMinMax() {
return this.param.valueMode === ParamValueMode.MINMAX;
}
public get isListe() {
return this.param.valueMode === ParamValueMode.LISTE;
}
/**
* renders model's numbers list as text values list (semicolon separated)
*/
public get valuesList() {
return (this.param.valueList || []).join(";");
}
/**
* injects text values list into model's numbers list
*/
public set valuesList(list: string) {
const vals = [];
const separatorRE = new RegExp(this.separatorPattern);
const parts = list.trim().split(separatorRE);
parts.forEach((e) => {
if (e.length > 0) {
// ensure decimal separator is "." for Number()
if (this.decimalSeparator !== ".") {
const re = new RegExp(this.decimalSeparator, "g"); // @TODO remove "g" ?
e = e.replace(re, ".");
}
vals.push(Number(e));
}
});
this.param.setValueList(this, vals);
}
public toggleViewChart() {
// validate list values before switching views ?
if (! this.viewChart && this.param.valueMode === ParamValueMode.LISTE) {
if (this.onValidate(false)) {
// toggle
this.viewChart = ! this.viewChart;
}
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
} else {
// toggle
this.viewChart = ! this.viewChart;
}
// refresh chart when displaying it only
if (this.viewChart) {
this.drawChart();
}
}
public onValidate(close = true) {
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;
if (close) {
this.dialogRef.close();
}
return true;
} else {
this.valuesListForm.controls.valuesList.setErrors({ "model": status.message });
return false;
}
}
/**
* 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
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;
}
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
return { ok, message };
}
public onFileSelected(event: any) {
if (event.target.files && event.target.files.length) {
const fr = new FileReader();
fr.onload = () => {
this.valuesListForm.controls.valuesList.setErrors(null);
// this.valuesList = String(fr.result);
this.valuesListForm.controls.valuesList.setValue(String(fr.result));
};
fr.onerror = () => {
fr.abort();
throw new Error("Erreur de lecture du fichier");
};
fr.readAsText(event.target.files[0]);
}
}
public onValueModeChange(event) {
this.initVariableValues();
}
private initVariableValues() {
// init min / max / step
if (this.isMinMax) {
if (this.param.minValue === undefined) {
this.param.setMinValue(this, this.param.getValue() / 2);
}
if (this.param.maxValue === undefined) {
this.param.setMaxValue(this, this.param.getValue() * 2);
}
let step = this.param.stepValue;
if (step === undefined) {
step = (this.param.maxValue - this.param.minValue) / 20;
}
this.param.setStepValue(this, step);
}
// init values list
if (this.isListe) {
if (this.param.valueList === undefined) {
if (this.param.isDefined) {
this.param.setValueList(this, [ this.param.getValue() ]);
} else {
this.param.setValueList(this, []);
}
// set form control initial value
this.valuesListForm.controls.valuesList.setValue(this.valuesList);
}
}
}
/**
* (re)Génère le graphique d'évolution des valeurs
*/
private drawChart() {
const data = [];
let i = 0;
for (const v of this.param.valuesIterator) {
data.push({
x: i,
y: v
});
i++;
}
this.chartData = {
datasets: [{
label: "",
data: data,
351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
borderColor: "#808080", // couleur de la ligne
backgroundColor: "rgba(0,0,0,0)", // couleur de remplissage sous la courbe : transparent
showLine: "true"
}]
};
}
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();
}
}