Le modal dialog sono uno strumento molto usato nelle applicazioni per interagire con l’utente. Purtroppo, mentre in una applicazione Windows esse sono tutto sommato semplici da gestire dato che il framework ci mette a disposizione più o meno tutto ciò di cui abbiamo bisogno, così non è nelle applicazione web in cui abbiamo solo alert e confirm che sono quanto di meno user friendly si possa immaginare. Inoltre, inserire delle proprie dialog è più o meno complicato in quando richiede non solo l’inserimento del codice Javascript che le gestisce, ma anche sel markup HTML. Un doppio lavoro quindi che complica non poco la questione. Personalmente ho trovato molto proficuo usare un servizio di AngularJS, scritto in Typescript, per cercare di risolvere questo problema alla radice. Per farlo mi sono appoggiato alle Modal popop di bootstrap che sono semplici ed efficaci e gestiscono molto efficacemente la responsività. Vediamo il servizio come è realizzato:

   1: module sys.services
   2: {
   3:     export class Constants
   4:     {
   5:         static containerId: string = '#modalContainer';
   6:         static defaultModalTitle: string = 'Message';
   7:         static modalDialogId: string = '#modalDialog';
   8:     
   9:         static Negative: string = '0';
  10:         static Positive: string = '1';
  11:         static Cancel: string = '2';
  12:     
  13:         static modalYesNo: sys.services.IDialogCommand[] = [
  14:             { id: Constants.Negative, text: 'No' },
  15:             { id: Constants.Positive, text: 'Yes', style: 'btn-primary' }];
  16:         static modalYesNoCancel: sys.services.IDialogCommand[] = [
  17:             { id: Constants.Cancel, text: 'Cancel' },
  18:             { id: Constants.Negative, text: 'No' },
  19:             { id: Constants.Positive, text: 'Yes', style: 'btn-primary' }];
  20:         static modalOkCancel: sys.services.IDialogCommand[] = [
  21:             { id: Constants.Cancel, text: 'Cancel' },
  22:             { id: Constants.Positive, text: 'Ok', style: 'btn-primary' }];
  23:     
  24:         static containerMarkup: string =
  25:         '<span></span>';
  26:         static modalMarkup: string =
  27:         '<div id="modalDialog" class="modal fade"><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><h4 class="modal-title">Modal title</h4></div><div class="modal-body"><p>One fine body&hellip;</p></div><div class="modal-footer"></div></div></div></div>';
  28:         static modalButtonMarkup: string =
  29:         '<button type="button" class="btn" data-dismiss="modal">Close</button>';
  30:     }
  31:     
  32:     export interface IDialogCommand
  33:     {
  34:         id: string;
  35:         text: string;
  36:         style?: string;
  37:     }
  38:  
  39:     export class ModalManager
  40:     {
  41:         private container: JQuery;
  42:         private modalDialog: JQuery;
  43:  
  44:         constructor()
  45:         {
  46:             this.appendModals();
  47:         }
  48:  
  49:         private exists(name: string): boolean
  50:         {
  51:             if (this.container == undefined)
  52:                 this.container = $(Constants.containerId);
  53:  
  54:             return this.container.find(name).length != 0;
  55:         }
  56:  
  57:         private appendModals(): void
  58:         {
  59:             if (!this.exists(Constants.containerId))
  60:             {
  61:                 this.container = $(Constants.containerMarkup);
  62:                 this.container.appendTo($(document.body));
  63:             }
  64:  
  65:             if (!this.exists(Constants.modalDialogId))
  66:             {
  67:                 this.modalDialog = $(Constants.modalMarkup);
  68:                 this.modalDialog.appendTo(this.container);
  69:             }
  70:         }
  71:  
  72:         public showDialog(message: string, title: string = Constants.defaultModalTitle, commands?: IDialogCommand[], callback?: (id: string) => void)
  73:         {
  74:             this.modalDialog.find('.modal-body').text(message);
  75:             this.modalDialog.find('.modal-title').text(title);
  76:  
  77:             var footer = this.modalDialog.find('.modal-footer');
  78:             footer.empty();
  79:  
  80:             for (var i in commands)
  81:             {
  82:                 var cmd = commands[i];
  83:  
  84:                 var button = $(Constants.modalButtonMarkup);
  85:  
  86:                 button.text(cmd.text);
  87:                 button.addClass(cmd.style == undefined ? 'btn-default' : cmd.style);
  88:                 button.data('command-id', cmd.id);
  89:  
  90:                 button.click((ev) =>
  91:                 {
  92:                     var id = $(event.srcElement).data('command-id');
  93:  
  94:                     this.modalDialog.modal('hide');
  95:  
  96:                     if (callback != undefined)
  97:                         callback.apply(this, [id]);
  98:                 });
  99:  
 100:                 button.appendTo(footer);
 101:             }
 102:  
 103:             this.modalDialog.modal('show');
 104:         }
 105:     }
 106: }

Il servizio così impostato è completo di tutto ciò che serve al funzionamento. Da solo per scontato che siano reparibili le librerie suddette (AngularJS e Bootstrap). Il metodo appendModals() in particolare si occupa di creare il markup necessario utilizzando alcune stringhe costanti. Esso verifica che il markup non sia già presente ed eventualmente lo inserisce aggiungendolo al body. Questo favorisce l’utilizzo della libreria che non richiede alcuna gestione del markup HTML. L’utilizzo è molto semplice. E’ sufficiente infatti registrare il servizio assieme al controller con la seguente riga:

   1: .service('$modalManager',
   2:     () => new sys.services.ModalManager());

Avremo così a disposizione il servizio $modalManager che possiamo iniettare in un controller. A questo punto, potremo richiamare la gdialog molto semplicemente usando uno dei metodi del servizio:

   1: this.$modalManager.showDialog(
   2:     'Are you sure you want to delete the resource',
   3:     'Confirm operation',
   4:     sys.Constants.modalYesNo,
   5:     (id: string) =>
   6:     {
   7:         if (id == sys.Constants.Positive)
   8:         {
   9:             this.clocks.push(timezone);
  10:         }
  11:  
  12:         this.selectedTimezone = undefined;
  13:         this.scope().$apply();
  14:     });

La dialog è sufficientemente intelligente da gestire numerose combinazioni di pulsanti. Le costanti presenti nella dichiarazione sono di aiuto in questo ma se ne possono creare di proprie dato che si tratta a tutti gli effetti di un array di IDialogCommand.

   1: static modalOkCancel: sys.services.IDialogCommand[] = [
   2:     { id: Constants.Cancel, text: 'Cancel' },
   3:     { id: Constants.Positive, text: 'Ok', style: 'btn-primary' }];

Senza dimenticare l’uso appropriato degli stili di bootstrap.