Skip to content
Snippets Groups Projects
app.component.ts 11.3 KiB
Newer Older
import { Component, ApplicationRef, OnInit, OnDestroy, HostListener, ViewChild, ComponentRef } from '@angular/core';
//import { MdDialog } from '@angular/material';
import { Router, ActivatedRoute } from '@angular/router';
francois.grand's avatar
francois.grand committed

import { environment } from '../environments/environment';
import { InternationalisationService, Language, LanguageCode } from './services/internationalisation/internationalisation.service';
import { ErrorService } from './services/error/error.service';
// import { AlertDialog } from './components/alert-dialog/alert-dialog.component';
import { FormulaireService } from './services/formulaire/formulaire.service';
import { FormulaireDefinition } from './formulaire/definition/form-definition';
import { ServiceFactory } from './services/service-factory';
import { ParamService } from './services/param/param.service';
import { ApplicationSetupService } from './services/app-setup/app-setup.service';
import { HttpService } from './services/http/http.service';
import { HelpService } from './services/help/help.service';
import { HelpComponent } from './components/help/help.component';
import { LoadCalcDialogAnchorDirective } from './components/load-calculator/load-calculator-anchor.directive';
import { LoadCalculatorComponent } from './components/load-calculator/load-calculator.component';
import { SaveCalcDialogAnchorDirective } from './components/save-calculator/save-calculator-anchor.directive';
import { SaveCalculatorComponent } from './components/save-calculator/save-calculator.component';
francois.grand's avatar
francois.grand committed
@Component({
francois.grand's avatar
francois.grand committed
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
francois.grand's avatar
francois.grand committed
})
export class AppComponent implements OnInit, OnDestroy, Observer {
  private _displayErrorDialog: boolean = false;

  /**
   * liste des calculettes. Forme des objets :
   * "title": nom de la calculette
   * "uid": id unique du formulaire
   */
  private _calculators: any[] = [];

  /**
   * id du formulaire courant
   * on utilise pas directement FormulaireService.currentFormId pour éviter l'erreur ExpressionChangedAfterItHasBeenCheckedError
   */
  private _currentFormId: number;

  @ViewChild(LoadCalcDialogAnchorDirective)
  private loadCalcDialogAnchor: LoadCalcDialogAnchorDirective;

  @ViewChild(SaveCalcDialogAnchorDirective)
  private saveCalcDialogAnchor: SaveCalcDialogAnchorDirective;

  /**
   * composant actuellement affiché par l'élément <router-outlet>
   */
  private _routerCurrentComponent: Component;

  constructor(private intlService: InternationalisationService,
    private paramService: ParamService,
    private appSetupService: ApplicationSetupService,
    private appRef: ApplicationRef,
    private errorService: ErrorService,
    private router: Router,
    private formulaireService: FormulaireService,
    private httpService: HttpService,
    private helpService: HelpService
    ServiceFactory.instance.applicationSetupService = appSetupService;
    ServiceFactory.instance.paramService = paramService;
    ServiceFactory.instance.internationalisationService = intlService;
    ServiceFactory.instance.formulaireService = formulaireService;
    ServiceFactory.instance.httpService = httpService;
    ServiceFactory.instance.helpService = helpService;
  // process.on('unhandledRejection', (reason, p) => {
  //   console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
  //   // Stack Trace
  //   console.log(reason.stack);
  // });
    this.intlService.addObserver(this);
    this.intlService.setLocale("fr");
    this.formulaireService.addObserver(this);
  ngOnDestroy() {
    this.unsubscribeErrorService();
    this.formulaireService.removeObserver(this);
  }

  private get uitextSidenavNewCalc() {
    return this.intlService.localizeText("INFO_MENU_NOUVELLE_CALC");
  }

  private get uitextSidenavParams() {
    return this.intlService.localizeText("INFO_SETUP_TITLE");
  }

  private get uitextSidenavLoadSession() {
    return "Charger une session";
  }

  /**
   * abonnement au service d'erreurs
   */
  private subscribeErrorService() {
    this.errorService.addObserver(this);
  private unsubscribeErrorService() {
    this.errorService.removeObserver(this);
  }

    let tag = this.intlService.currentLanguage.tag;
    document['locale'] = tag;

    // location.reload(true);
    // this.cdRef.markForCheck();
    // this.cdRef.detectChanges();
    this.appRef.tick();
  }

  update(sender: any, data: any): void {
    if (sender instanceof ErrorService) {
      // on ouvre un dialogue avec le message d'erreur reçu
      // if (this._displayErrorDialog) {
      //   let dialogRef = this.dialog.open(AlertDialog);
      //   let ad: AlertDialog = dialogRef.componentInstance;
      //   ad.text = String(data);
      // }
      // else
      console.log(data);
    }
    else if (sender instanceof FormulaireService) {
        case "createForm":
          const f: FormulaireDefinition = data["form"];
          this._calculators.push(
            {

          // abonnement en tant qu'observateur du nouveau formulaire
          f.addObserver(this);
        case "invalidFormId":
        case "currentFormChanged":
          /*
           utilisation de setTimeout() pour éviter le message console ExpressionChangedAfterItHasBeenCheckedError
           relatif au getter getHighlightClass() (qui change de valeur quand le formulaire courant change)
           */
          setTimeout(() => {
            this._currentFormId = data["formId"];
          }, 1);
          break;

        case "closeForm":
          const form: FormulaireDefinition = data["form"];
          this.closeCalculator(form);
    else if (sender instanceof InternationalisationService) {
      this.updateLocale();
    }
    else if (sender instanceof FormulaireDefinition) {
      switch (data["action"]) {
        case "nameChanged":
          this.updateCalculatorTitle(sender, data["name"]);
          break;
  private getCalculatorIndexFromId(formId: number) {
    const index = this._calculators.reduce((resultIndex, calc, currIndex) => {
      if (resultIndex == -1 && calc["uid"] == formId)
        resultIndex = currIndex;
      return resultIndex;
    }, -1);

    return index;
  }

  private updateCalculatorTitle(f: FormulaireDefinition, title: string) {
    const formIndex = this.getCalculatorIndexFromId(f.uid);
    this._calculators[formIndex]["title"] = title;
  }

  /**
   * sauvegarde du/des formulaires
   * @param form formulaire à sélectionner par défaut dans la liste
   */
  private saveForm(form: FormulaireDefinition) {
    // création du dialogue de sélection des formulaires à sauver
    const compRef: ComponentRef<SaveCalculatorComponent> = this.saveCalcDialogAnchor.createDialog();

    // création de la liste des formulaires
    let list = [];
    for (const c of this._calculators) {
      const uid = +c["uid"];
      list.push({
        "selected": uid === form.uid,
        "title": c["title"],
        "uid": uid
      })
    }

    // passage de la liste, récupération d'une Promise pour traiter le résultat
    const prom: Promise<any[]> = compRef.instance.run(list);
    prom.then(list => {
      let name = compRef.instance.filename;

      // ajout extension ".json"
      const re = /.+\.json/;
      const match = re.exec(name.toLowerCase());
      if (match === null)
        name = name + ".json";

      this.saveSession(list, name)
  private saveSession(calcList: any[], filename) {
        const form: FormulaireDefinition = this.formulaireService.getFormulaireFromId(c.uid);
        elems.push(form.JSONserialise());
    let session = { "session": { "elements": elems } };
    this.formulaireService.saveSession(session, filename);
  private closeCalculator(form: FormulaireDefinition) {
    const formId: number = form.uid;

    // désabonnement en tant qu'observateur

    form.removeObserver(this);

    // recherche de la calculette correspondante à formId

    const closedIndex = this.getCalculatorIndexFromId(formId);

    /*
     * détermination de la nouvelle calculette à afficher : 
     * - celle après celle supprimée
     * - ou celle avant celle supprimée si on supprime la dernière
     */

    let newId: number = -1;
    const l = this._calculators.length;
    if (l > 1) {
      if (closedIndex == l - 1)
        newId = +this._calculators[closedIndex - 1]["uid"];
      else
        newId = +this._calculators[closedIndex + 1]["uid"];
    }

    // suppression

    this._calculators = this._calculators.filter(calc => {
      return formId != +calc["uid"]
    });

    // MAJ affichage

    if (newId == -1) {
      this._currentFormId = -1;
    }
    else
      this.toCalc(newId);
  }

  private toList() {
    this.router.navigate(['/list']);
  }

  private toCalc(id: number) {
    this.router.navigate(['/calculator', id]);
  }

  private toHelp(form: FormulaireDefinition) {
    // Activated.firstChild.component : type du composant actuellement affiché par le router outlet
    this.router.navigate(['/help']).then(_ => {
      const helpComp = this._routerCurrentComponent as HelpComponent;
      helpComp.formHelp = form;
    })
  }

  /**
   * récupération du composant affiché par le routeur
   */
  private onRouterOutletActivated(a) {
    this._routerCurrentComponent = a;
  private getHighlightClass(uid: number) {
    return uid == this._currentFormId ? "blue darken-2" : "";
  // flag d'affichage des repères des colonnes Bootstrap : uniquement en mode dev
  // cf. src/environments/*, ng build --env=<mode> (par ex : ng build --env=prod)
  private get ruler(): boolean {
    // return !environment.production;
    return false;
  // sidenav

  private openNav() {
    document.getElementById("mySidenav").style.width = "300px";
  }

  private closeNav() {
    document.getElementById("mySidenav").style.width = "0";
  }

  private newCalc() {
    this.closeNav();
  private loadSession() {
    this.closeNav();

    // création du dialogue de sélection des formulaires à sauver
    const compRef: ComponentRef<LoadCalculatorComponent> = this.loadCalcDialogAnchor.createDialog();

    const prom: Promise<any[]> = compRef.instance.run();
    prom.then(list => {
      this.formulaireService.loadSession(compRef.instance.selectedFile, compRef.instance.calculators);
  private params() {
    this.closeNav();
    this.router.navigate(['/setup']);
  }

  // /sidenav

  /**
   * détection de la fermeture de la page/navigateur et demande de confirmation
   */
  @HostListener('window:beforeunload', ['$event'])
  confirmExit($event) {
    if (environment.production) {
      // affecter une valeur différente de null provoque l'affichage d'un dialogue de confirmation, mais le texte n'est pas affiché
      $event.returnValue = 'Your data will be lost !';
    }