An error occurred while loading the file. Please try again.
-
Midoux Cedric authoredbc7f82d5
import { Input, Output, EventEmitter, ChangeDetectorRef, OnChanges, ViewChild } from "@angular/core";
import { NgModel } from "@angular/forms";
import { BaseComponent } from "../base/base.component";
import { isNumeric, Structure, PabCloisons } from "jalhyd";
import { FormulaireDefinition } from "../../formulaire/definition/form-definition";
import { NgParameter } from "../../formulaire/ngparam";
import { I18nService } from "../../services/internationalisation/internationalisation.service";
/**
* classe de gestion générique d'un champ de saisie avec titre, validation et message d'erreur
* définitions :
* - modèle : entité mémoire gérée, indépendamment de la façon dont elle est affichée.
* A noter que si cette entité est une classe, on peut ne présenter à l'interface qu'un membre de cette classe,
* cad que get model() et getModelValue() ne renverront pas la même chose.
* Par ex : get model()-> instance_de_la_classe_Toto, getModelValue() -> Toto.unMembreNumerique
* - valeur gérée : entité elle même si c'est un type simple (number, string, ...) ou une partie d'un classe
* - UI : interface utilisateur, présentation de la valeur gérée
*/
export abstract class GenericInputComponent extends BaseComponent implements OnChanges {
/**
* entité mémoire gérée
*/
protected _model: NgParameter | FormulaireDefinition; // CalcName utilise un FormDefinition ici !?
/**
* flag de désactivation de l'input
*/
@Input()
private _inputDisabled = false;
/**
* flag d'affichage du message d'erreur
*/
public showError = true;
/**
* id de l'input, utilisé notamment pour les tests
*/
public get inputId() {
let id = "error-in-inputId";
if (this._model) {
// unique input id based on parameter symbol
if (this._model instanceof NgParameter) {
const param = this._model as NgParameter;
id = param.symbol;
// if inside a nested Structure, prefix with Structure position
// to disambiguate
const nub = param.paramDefinition.parentNub;
if (nub && (nub instanceof Structure || nub instanceof PabCloisons)) {
id = nub.findPositionInParent() + "_" + id;
}
}
}
return id;
}
/**
* chaîne affichée dans l'input quand aucune valeur n'est saisie
*/
@Input()
public title: string;
/**
* événement signalant un changement : valeur du modèle, validité, ...
*/
@Output()
protected change = new EventEmitter<any>();
/**
* événement signalant un appui sur TAB ou SHIFT+TAB
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
*/
@Output()
protected tabPressed = new EventEmitter<any>();
/**
* valeur saisie.
* Cette variable n'est modifiée que lorsqu'on affecte le modèle ou que l'utilisateur fait une saisie
*/
private _uiValue: string;
/**
* 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 _isValidUI = false;
/**
* 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 _isValidModel = false;
/**
* message d'erreur UI
*/
private _errorMessageUI: string;
/**
* message d'erreur modèle
*/
private _errorMessageModel: string;
@ViewChild("inputControl") inputField: NgModel;
constructor(private cdRef: ChangeDetectorRef, protected intlService: I18nService) {
super();
}
public get isDisabled(): boolean {
return this._inputDisabled;
}
/**
* événement de changement de la validité de la saisie
*/
private emitValidChanged() {
this.change.emit({ "action": "valid", "value": this.isValid });
}
/**
* détection des changements dans l'UI par le ChangeDetector du framework
*/
protected detectChanges() {
if (this.cdRef) {
// if (!this.cdRef['destroyed']) // pour éviter l'erreur "Attempt to use a destroyed view: detectChanges"
// this.cdRef.detectChanges();
this.cdRef.markForCheck();
}
}
/**
* calcul de la validité globale du composant (UI+modèle)
*/
public get isValid() {
return this._isValidUI && this._isValidModel;
}
private setUIValid(b: boolean) {
const old = this.isValid;
this._isValidUI = b;
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
if (this.isValid !== old) {
this.emitValidChanged();
}
}
protected validateUI() {
const { isValid, message } = this.validateUIValue(this._uiValue);
this._errorMessageUI = message;
this.detectChanges();
this.setUIValid(isValid);
return isValid;
}
private setModelValid(b: boolean) {
const old = this.isValid;
this._isValidModel = b;
if (this.isValid !== old) {
this.emitValidChanged();
}
// répercussion des erreurs sur le Form angular, pour faire apparaître/disparaître les mat-error
if (b) {
this.inputField.control.setErrors(null);
} else {
this.inputField.control.setErrors({ "incorrect": true });
}
}
private validateModel() {
const { isValid, message } = this.validateModelValue(this.getModelValue());
this._errorMessageModel = message;
this.detectChanges();
this.setModelValid(isValid);
}
public validate() {
this.validateUI();
this.validateModel();
}
/**
* getter du message d'erreur affiché.
* L'erreur de forme (UI) est prioritaire
*/
public get errorMessage() {
if (this._errorMessageUI !== undefined) {
return this._errorMessageUI;
}
return this._errorMessageModel;
}
public get model(): any {
return this._model;
}
/**
* événement de changement de la valeur du modèle
*/
private emitModelChanged() {
this.change.emit({ "action": "model", "value": this.getModelValue() });
}
protected setAndValidateModel(sender: any, v: any) {
this.setModelValue(sender, v);
this.emitModelChanged();
this.validateModel();
}
public set model(v: any) {
this.beforeSetModel();
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
this._model = v;
this.afterSetModel();
this.updateAll();
this.detectChanges();
}
/**
* MAJ et validation de l'UI
*/
protected updateAndValidateUI() {
this._uiValue = String(this.getModelValue());
this.validateUI();
}
public get uiValue() {
return this._uiValue;
}
/*
* fonction appelée lorsque l'utilisateur fait une saisie
* @param ui valeur dans le contrôle
*/
public set uiValue(ui: any) {
this._uiValue = ui;
this.updateModelFromUI();
}
/**
* met à jour le modèle d'après la saisie
*/
public updateModelFromUI() {
if (this.validateUI()) {
this.setAndValidateModel(this, +this._uiValue); // cast UI value to Number
}
}
/**
* Renvoie l'événement au composant du dessus
*/
public onTabPressed(event, shift: boolean) {
this.tabPressed.emit({ originalEvent: event, shift: shift });
return false; // stops event propagation
}
private updateAll() {
this.updateAndValidateUI();
this.validateModel();
}
/**
* appelé quand les @Input changent
*/
public ngOnChanges() {
this.updateAll();
}
/**
* appelé avant le changement de modèle
*/
protected beforeSetModel() { }
/**
* appelé après le changement de modèle
*/
protected afterSetModel() { }
/**
* retourne la valeur du modèle
*/
protected abstract getModelValue(): any;
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
/**
* affecte la valeur du modèle
*/
protected abstract setModelValue(sender: any, v: any);
/**
* valide une valeur de modèle : est ce une valeur acceptable ? (par ex, nombre dans un intervalle, valeur dans une liste, ...)
* @param v valeur à valider
* @returns isValid : true si la valeur est valide, false sinon
* @returns message : message d'erreur
*/
protected abstract validateModelValue(v: any): { isValid: boolean, message: string };
/**
* valide une valeur saisie dans l'UI (forme de la saisie : est ce bien une date, un nombre, ...)
* @param ui saisie à valider
* @returns isValid : true si la valeur est valide, false sinon
* @returns message : message d'erreur
*/
protected validateUIValue(ui: string): { isValid: boolean, message: string } {
let valid = false;
let msg: string;
if (! isNumeric(ui)) {
msg = this.intlService.localizeText("ERROR_PARAM_MUST_BE_A_NUMBER");
} else {
valid = true;
}
return { isValid: valid, message: msg };
}
}