// cf. https://blog.thoughtram.io/angular/2016/07/27/custom-form-controls-in-angular-2.html import { Component, Input, forwardRef, OnInit, DoCheck, ChangeDetectorRef } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, NG_VALIDATORS, FormControl } from '@angular/forms'; import { ComputeNodeType, ParamDefinition, NumericalString, Message } from 'jalhyd'; import { ParamService } from '../../services/param/param.service'; import { InternationalisationService, LanguageCode } from '../../services/internationalisation/internationalisation.service'; import { NgParameter } from '../../calculators/generic/ngparam'; @Component({ selector: 'param-input', /* OK <input placeholder="{{_paramDef.symbol}}" [ngModel]="_paramDef.v" (ngModelChange)="setValue($event)"/> <p *ngIf="_message">{{_message}}</p> */ templateUrl: "./param-input.component.html", providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => ParamInputComponent), multi: true }, { provide: NG_VALIDATORS, useExisting: forwardRef(() => ParamInputComponent), multi: true } ] }) export class ParamInputComponent implements ControlValueAccessor, OnInit, DoCheck { /** * type de noeud de calcul */ private _computeNode: ComputeNodeType; @Input() private set computeNodeType(t: ComputeNodeType) { this._computeNode = t; } /** * Parameter symbol (Q, Ks, B, ...) attribute */ @Input('symbol') private _paramSymbol: string; /** * enable/disable input field */ @Input('inputDisabled') private _inputDisabled: boolean; /** * managed parameter */ private _paramDef: NgParameter; private _message: string; /** * flag d'affichage du titre */ public displayTitle: boolean = false; /** * true si la modification du paramètre géré vient de l'interface utilisateur * * false si la modification du paramètre géré vient d'un appel du code */ private _fromUI: boolean; /** * valeur dans le contrôle (saisie par l'utilisateur) */ private _uiValue: NumericalString; constructor(private paramService: ParamService, private changeDetector: ChangeDetectorRef, private intlService: InternationalisationService) { this._uiValue = new NumericalString(); } hasError(): boolean { let res = (this._message != undefined); // if (res) // this.log("hasError : true " + this._message); // else // this.log("hasError : false"); return res; } private getSfromUI(): string { return this._fromUI ? " fromUI " : " fromMODEL"; } private getSParam(): string { return " " + this._paramDef.symbol + "=" + this._paramDef.toString() } private getSUIvalue(v: NumericalString = undefined): string { if (v == undefined) return " uiValue=" + this._uiValue.toString() + ""; return " uiValue=" + v.toString() + ""; } ngOnInit() { // retrieve parameter from symbol this._paramDef = this.paramService.getParameter(this._computeNode, this._paramSymbol); } /** * fonction appelée lorsque l'utilisateur fait une saisie * @param event valeur du contrôle */ private setValue(event: any) { this._fromUI = true; this._uiValue.value = event; // this.log(this._uiValue.toString()); return this.validateUIValue(); } /** * fonction appelée lors d'un rafraîchissement de l'UI */ ngDoCheck(): void { // this.log("ngDoCheck start : " + this.getSParam() + this.getSUIvalue() + this.getSfromUI()); if (this._fromUI) this.updateMessage(this._uiValue); else { if (this._paramDef.isDefined) { this.updateMessage(new NumericalString(this._paramDef.getValue())); this._uiValue.value = String(this._paramDef.getValue()); } else this.updateMessage(this._uiValue); } // this.log("ngDoCheck end : " + this.getSParam() + this.getSUIvalue()); this._fromUI = false; } private updateMessage(v: NumericalString) { // this.log("updateMessage start :" + this.getSParam() + this.getSfromUI() + this.getSUIvalue(v) + " message=" + this._message); if (v.isNumerical) { this._message = undefined; try { this._paramDef.checkValue(v.numericalValue); } catch (e) { if (e instanceof Message) this._message = this.intlService.localizeMessage(e); else this._message = "invalid value"; } } else { switch (this.intlService.currentLanguage.code) { case LanguageCode.FRENCH: this._message = "Veuillez entrer une valeur numérique"; break; default: this._message = "Please enter a numerical value"; } } // this.log("updateMessage end :" + this.getSParam() + this.getSfromUI() + this.getSUIvalue(v) + " message=" + this._message); } private validateUIValue() { // this.log(""); // this.log("validateValue start : val '" + this._uiValue.toString() + "'" + this.getSParam() + this.getSfromUI()); let ok: boolean = this._uiValue.isNumerical; if (ok) { try { if (!this._paramDef.isDefined || this._paramDef.getValue() != this._uiValue.numericalValue) { this._paramDef.setValue(this._uiValue.numericalValue); this.changeDetector.detectChanges(); // provoque une détection des changements dans les contrôles } } catch (e) { ok = false; } } if (!ok) { // this.log("validateValue end : " + this.getSParam()); let err = { rangeError: { // given: val, given: this._uiValue.toString(), max: 4, min: 0 } }; return err; } // this.log("validateValue end : " + this.getSParam()); return null; } // private log(m: string) { // console.log("ParamInputComponent(" + this._id + ") : " + m); // } // ControlValueAccessor interface propagateChange = (_: any) => { }; /* //From ControlValueAccessor interface writeValue(value: any) { if (value !== this.innerValue) { this.innerValue = value; } } */ writeValue(value: any) { // this.log("writeValue " + value); } registerOnChange(fn: any) { this.propagateChange = fn; } registerOnTouched() { } }