calculator.component.ts 18.63 KiB
import { Component, OnInit, DoCheck, OnDestroy, ViewChild, ViewChildren,
         QueryList, AfterViewChecked, ElementRef } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { Observer, Session, Cloisons, Pab, ParamValueMode, CalculatorType } from "jalhyd";
import { FormulaireService } from "../../services/formulaire/formulaire.service";
import { I18nService } from "../../services/internationalisation/internationalisation.service";
import { FieldSet } from "../../formulaire/fieldset";
import { FormulaireDefinition } from "../../formulaire/definition/form-definition";
import { CalculatorResultsComponent } from "../../components/calculator-results/calculator-results.component";
import { Subscription } from "rxjs";
import { FieldSetComponent } from "../field-set/field-set.component";
import { BaseComponent } from "../base/base.component";
import { CalculatorNameComponent } from "./calc-name.component";
import { FormulaireElement } from "../../formulaire/formulaire-element";
import { FieldsetContainer } from "../../formulaire/fieldset-container";
import { FieldsetContainerComponent } from "../fieldset-container/fieldset-container.component";
import { PabTableComponent } from "../pab-table/pab-table.component";
import { ServiceFactory } from "../../services/service-factory";
import { MatDialog } from "@angular/material";
import { DialogConfirmCloseCalcComponent } from "../dialog-confirm-close-calc/dialog-confirm-close-calc.component";
import { DialogGeneratePABComponent } from "../dialog-generate-pab/dialog-generate-pab.component";
import { PabTable } from "../../formulaire/pab-table";
@Component({
    selector: "hydrocalc",
    templateUrl: "./calculator.component.html",
    styleUrls: ["./calculator.component.scss"]
export class GenericCalculatorComponent extends BaseComponent implements OnInit, DoCheck, AfterViewChecked, OnDestroy, Observer {
    /**
     * liste des FieldSetComponent
    @ViewChildren(FieldSetComponent)
    private _fieldsetComponents: QueryList<FieldSetComponent>;
    /**
     * liste des FieldsetContainerComponent
    @ViewChildren(FieldsetContainerComponent)
    private _fieldsetContainerComponents: QueryList<FieldsetContainerComponent>;
    /**
     * PabTableComponent if any
    @ViewChild(PabTableComponent, { static: false })
    private _pabTableComponent: PabTableComponent;
    /**
     * composant d'affichage des résultats
    @ViewChild(CalculatorResultsComponent, { static: true })
    private resultsComponent: CalculatorResultsComponent;
    /**
     * composant "nom du module de calcul"
    @ViewChild(CalculatorNameComponent, { static: true })
    private _calculatorNameComponent: CalculatorNameComponent;
    /**
     * formulaire affiché
    private _formulaire: FormulaireDefinition;
    private _subscription: Subscription; // pour souscrire aux changements d'URL envoyés par le routeur
    /**
     * true si on a cliqué sur le bouton de lancement de calcul
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
*/ 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 = false; /** * flag disabled du bouton "calculer" */ public isCalculateDisabled = true; /** * flag (+info) indiquant un événement radio à traiter * nécessaire avec l'introduction du mode de valeur LINK des paramètres car quand on modifie le mode à LINK, les possibles * paramètres liables ne sont pas encore connus */ private _pendingRadioClick = false; private _pendingRadioClickInfo: any; // services private intlService: I18nService; private formulaireService: FormulaireService; public get ID() { if (this._formulaire) { return this._formulaire.uid; } else { return "calculator_1"; } } constructor( private route: ActivatedRoute, private router: Router, private confirmCloseCalcDialog: MatDialog, private generatePABDialog: MatDialog, private _elementRef: ElementRef ) { super(); this.intlService = ServiceFactory.instance.i18nService; this.formulaireService = ServiceFactory.instance.formulaireService; } public get formElements(): FormulaireElement[] { if (this._formulaire === undefined) { return []; } return this._formulaire.kids as FormulaireElement[]; } /** * détermine si un FormulaireElement est du type FieldSet */ public isFieldset(fe: any): boolean { return fe instanceof FieldSet; } /** * détermine si un FormulaireElement est du type FieldsetContainer */ public isFieldsetContainer(fe: any): boolean { return fe instanceof FieldsetContainer; }
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
/** * détermine si un FormulaireElement est du type PabTable */ public isPabTable(fe: any): boolean { return fe instanceof PabTable; } public get hasForm() { return this._formulaire !== undefined; } public get hasResults(): boolean { if (this.hasForm) { return this._formulaire.hasResults; } return false; } public get uitextTitre() { if (this.hasForm) { return this.formulaireService.getLocalisedTitleFromCalculatorType(this._formulaire.calculatorType); } } public get uitextCalculer() { return this.intlService.localizeText("INFO_CALCULATOR_CALCULER"); } public get uitextCalculatorName() { return this.intlService.localizeText("INFO_CALCULATOR_CALC_NAME"); } public get uitextResultsTitle() { return this.intlService.localizeText("INFO_CALCULATOR_RESULTS_TITLE"); } public get uitextGeneratePAB() { return this.intlService.localizeText("INFO_CALCULATOR_RESULTS_GENERATE_PAB"); } public get uitextOpenHelp() { return this.intlService.localizeText("INFO_CALCULATOR_OPEN_HELP"); } public get uitextCloneCalculator() { return this.intlService.localizeText("INFO_CALCULATOR_CLONE"); } public get uitextSaveCalculator() { return this.intlService.localizeText("INFO_CALCULATOR_SAVE"); } public get uitextCloseCalculator() { return this.intlService.localizeText("INFO_CALCULATOR_CLOSE"); } public get quicknavItems() { const elts = [ "input", "results" ]; if (this.isPAB && this.hasResults) { elts.push("charts"); } return elts; } /** * Triggered at calculator instanciation */ ngOnInit() { this.formulaireService.addObserver(this); this.subscribeRouter();
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
} ngDoCheck() { this.isCalculateDisabled = ! this._isUIValid; } ngOnDestroy() { this.unsubscribeRouter(); this.formulaireService.removeObserver(this); this.intlService.removeObserver(this); } private subscribeRouter() { // récupération des paramètres passés avec la route (URL) this._subscription = this.route.params.subscribe(params => { const uid: string = params["uid"]; this.formulaireService.setCurrentForm(uid); }); } private unsubscribeRouter() { if (this._subscription) { this._subscription.unsubscribe(); } } /* * gestion des événements clic sur les radios */ public onRadioClick(info: any) { if (info.param.valueMode === ParamValueMode.LINK) { this.updateLinkedParameters(); // only when switching to LINK mode } this._pendingRadioClick = true; this._pendingRadioClickInfo = info; } public ngAfterViewChecked() { if (this._pendingRadioClick) { this._pendingRadioClick = false; this._formulaire.onRadioClick(this._pendingRadioClickInfo); this._pendingRadioClickInfo = undefined; } } public onCloseForm() { this.formulaireService.requestCloseForm(this._formulaire.uid); } /** * relit les valeurs dans l'interface et met à jour les NgParameter */ private updateParametersFromUI() { this._fieldsetComponents.forEach(fsc => fsc.updateParametersFromUI()); this._fieldsetContainerComponents.forEach(fscc => fscc.updateParametersFromUI()); } /** * met à jour les paramètres liés */ private updateLinkedParameters() { this._fieldsetComponents.forEach(fsc => fsc.updateLinkedParameters()); this._fieldsetContainerComponents.forEach(fscc => fscc.updateLinkedParameters()); } public doCompute() { this._formulaire.doCompute(); this._computeClicked = true; }
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
public onCalcResultsViewChecked() { if (this._computeClicked) { this._computeClicked = false; this.scrollToResults(); } } private scrollToResults() { const element = document.getElementById ("fake-results-anchor"); if (element) { element.scrollIntoView(); } } private setForm(f: FormulaireDefinition) { if (this._formulaire !== undefined) { this._formulaire.removeObserver(this); } this._formulaire = f; if (this._formulaire !== undefined) { this._formulaire.addObserver(this); } } private updateFormulaireResults(uid: string) { if (this._formulaire.uid === uid) { this.resultsComponent.updateView(); } } // interface Observer update(sender: any, data: any): void { if (sender instanceof FormulaireService) { switch (data["action"]) { case "currentFormChanged": const uid: string = data["formId"]; this.setForm(this.formulaireService.getFormulaireFromId(uid)); this.resultsComponent.formulaire = this._formulaire; this._calculatorNameComponent.model = this._formulaire; // reload localisation in all cases this.formulaireService.loadUpdateFormulaireLocalisation(this._formulaire); break; } } else if (sender instanceof FormulaireDefinition) { let f: FormulaireDefinition; switch (data["action"]) { case "resetForm": // réinitialisation du formulaire case "resultsUpdated": f = sender as FormulaireDefinition; this.updateFormulaireResults(f.uid); break; } } } /** * appelé après le 1er affichage du composant */ protected afterFirstViewChecked() { this.updateUIValidity(); } /** * calcul de la validité globale de la vue */ private updateUIValidity() { this._isUIValid = false; if (this._fieldsetComponents !== undefined) {
351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
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 , this._fieldsetComponents.length > 0); } if (this._fieldsetContainerComponents !== undefined) { this._isUIValid = this._isUIValid && this._fieldsetContainerComponents.reduce<boolean>( // callback ( // accumulator (valeur précédente du résultat) acc, // currentValue (élément courant dans le tableau) fieldsetContainer, // currentIndex (indice courant dans le tableau) currIndex, // array (tableau parcouru) array ): boolean => { return acc && fieldsetContainer.isValid; } // valeur initiale , true); } if (this._pabTableComponent !== undefined) { this._isUIValid = this._isUIValid && this._pabTableComponent.isValid; } } public getFieldsetStyleDisplay(id: string) { const isDisplayed: boolean = this._formulaire.isDisplayed(id); return isDisplayed ? "block" : "none"; } /** * réception d'un événement de validité d'un FormElement */ public onElementValid() { this.updateUIValidity(); } /** * réception d'un événement de changement de valeur d'un input */ public onInputChange(event: any) { this._formulaire.resetResults([], (event ? event.symbol : undefined)); // to refresh log components, that are fed manually (!) this.resultsComponent.updateView(); } /** * Passe d'un champ de saisie à l'autre lors de l'appui sur * TAB, sans passer par les autres éléments d'interface */ public onTabPressed(event: any) { // event source input id const srcId = event.originalEvent.target.id;
421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
// find all available inputs const qs = "ngparam-input input.form-control"; let inputs: Node[] = Array.from(this._elementRef.nativeElement.querySelectorAll(qs)); // find calculated symbol if any, to exclude it from inputs list let calcSymbol = ""; if (this._formulaire.currentNub.calculatedParam) { calcSymbol = this._formulaire.currentNub.calculatedParam.symbol; } inputs = inputs.filter((i: any) => { return i.id !== calcSymbol; }); // find position among inputs list const currentIndex = inputs.findIndex((i: any) => { return i.id === srcId; }); // focus previous / next input let newIndex = currentIndex; if (event.shift) { newIndex = (newIndex === 0) ? (inputs.length - 1) : (newIndex - 1); } else { newIndex = (newIndex + 1) % inputs.length; } const elt = (inputs[newIndex] as HTMLInputElement); elt.focus(); elt.select(); } public openHelp() { window.open("assets/docs-fr/calculators/" + this._formulaire.helpLink + ".html", "_blank"); } /** * flag d'affichage du bouton d'aide */ public get enableHelpButton() { return this._formulaire && this._formulaire.helpLink; } // for "one wide column" layout public get isPAB() { return ( this._formulaire && this._formulaire.currentNub && this._formulaire.currentNub.calcType === CalculatorType.Pab ); } // for "generate PAB" button public get isPABCloisons() { return ( this._formulaire && this._formulaire.currentNub && this._formulaire.currentNub.calcType === CalculatorType.Cloisons ); } /** * Returns true if no parameter is varying */ private allParamsAreFixed() { let ret = true; for (const p of this._formulaire.currentNub.parameterIterator) { ret = ret && (! p.hasMultipleValues); } return ret; }
491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
// PAB generation is enabled only if : // - no parameter is varying // - Cloisons is calculated first public get generatePABEnabled(): boolean { return this.allParamsAreFixed() && this.hasResults; } public get uitextGeneratePabTitle() { if (! this.hasResults) { return this.intlService.localizeText("INFO_CALCULER_D_ABORD"); } if (! this.allParamsAreFixed) { return this.intlService.localizeText("INFO_PAB_PARAMETRES_FIXES"); } return ""; } /** * Génère une passe à bassins à partir du modèle de cloisons en cours */ public generatePAB() { // création du dialogue de génération d'une passe à bassin const cloisons = (this._formulaire.currentNub as Cloisons); const dialogRef = this.generatePABDialog.open( DialogGeneratePABComponent, { data: { chute: cloisons.prms.DH.V, debit: cloisons.prms.Q.V, coteAmont: cloisons.prms.Z1.V }, disableClose: true } ); dialogRef.afterClosed().subscribe(result => { if (result) { if (result.generate) { this.formulaireService.createFormulaire(CalculatorType.Pab).then((f: FormulaireDefinition) => { const pab = (f.currentNub as Pab); const params = pab.prms; // paramètres hydrauliques params.Q.singleValue = result.debit; params.Z1.singleValue = result.coteAmont; params.Z2.singleValue = result.coteAval; // création des bassins pab.deleteChild(0); pab.addCloisonsFromModel(this._formulaire.currentNub as Cloisons, result.nbBassins); // go to new PAB this.router.navigate(["/calculator", f.uid]); }); } } }); } public saveCalculator() { this.formulaireService.saveForm(this._formulaire); } public closeCalculator() { const dialogRef = this.confirmCloseCalcDialog.open( DialogConfirmCloseCalcComponent, { data: { uid: this._formulaire.currentNub.uid }, disableClose: true } );
561562563564565566567568569570571572573574575576577578579
dialogRef.afterClosed().subscribe(result => { if (result) { this.onCloseForm(); } }); } /** * Duplicates the current calculator form */ public cloneCalculator() { const serialisedNub: string = this._formulaire.currentNub.serialise({ title: this._formulaire.calculatorName }); const nubPointer = Session.getInstance().unserialiseSingleNub(serialisedNub); this.formulaireService.createFormulaire(nubPointer.nub.calcType, nubPointer.nub, nubPointer.meta.title).then((f) => { this.router.navigate(["/calculator", f.uid]); }); } }