]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/eg2/src/app/share/dialog/dialog.component.ts
LP#1398107: Add i18n attribute to alert text
[Evergreen.git] / Open-ILS / src / eg2 / src / app / share / dialog / dialog.component.ts
1 import {Component, Input, OnInit, ViewChild, TemplateRef, EventEmitter} from '@angular/core';
2 import {Observable, Observer} from 'rxjs';
3 import {NgbModal, NgbModalRef, NgbModalOptions} from '@ng-bootstrap/ng-bootstrap';
4
5 /**
6  * Dialog base class.  Handles the ngbModal logic.
7  * Sub-classed component templates must have a #dialogContent selector
8  * at the root of the template (see ConfirmDialogComponent).
9  *
10  * Dialogs interact with the caller via Observable.
11  *
12  * dialog.open().subscribe(
13  *   value => handleValue(value),
14  *   error => handleError(error),
15  *   ()    => console.debug('dialog closed')
16  * );
17  *
18  * It is up to the dialog implementer to decide what values to
19  * pass to the caller via the dialog.respond(data) and/or
20  * dialog.close(data) methods.
21  *
22  * dialog.close(...) closes the modal window and completes the
23  * observable, unless an error was previously passed, in which
24  * case the observable is already complete.
25  *
26  * dialog.close() with no data closes the dialog without passing
27  * any values to the caller.
28  */
29
30 @Component({
31     selector: 'eg-dialog',
32     template: '<ng-template></ng-template>'
33 })
34 export class DialogComponent implements OnInit {
35
36     // Track instances so we can refer to them later in closeAll()
37     // NOTE this could also be done by importing router and subscribing
38     // to route events here, but that would require all subclassed
39     // components to import and pass the router via the constructor.
40     static counter = 0;
41     static instances: {[ident: number]: any} = {};
42
43     // Assume all dialogs support a title attribute.
44     @Input() public dialogTitle: string;
45
46     // Pointer to the dialog content template.
47     @ViewChild('dialogContent', {static: false})
48     private dialogContent: TemplateRef<any>;
49
50     identifier: number = DialogComponent.counter++;
51
52     // Emitted after open() is called on the ngbModal.
53     // Note when overriding open(), this will not fire unless also
54     // called in the overridding method.
55     onOpen$ = new EventEmitter<any>();
56
57     // How we relay responses to the caller.
58     observer: Observer<any>;
59
60     // The modalRef allows direct control of the modal instance.
61     private modalRef: NgbModalRef = null;
62
63     constructor(private modalService: NgbModal) {}
64
65     // Close all active dialogs
66     static closeAll() {
67         Object.keys(DialogComponent.instances).forEach(id => {
68             if (DialogComponent.instances[id]) {
69                 DialogComponent.instances[id].close();
70                 delete DialogComponent.instances[id];
71             }
72         });
73     }
74
75     ngOnInit() {
76         this.onOpen$ = new EventEmitter<any>();
77     }
78
79     open(options: NgbModalOptions = { backdrop: 'static' }): Observable<any> {
80
81         if (this.modalRef !== null) {
82             this.error('Dialog was replaced!');
83             this.finalize();
84         }
85
86         // force backdrop to static if caller passed in any options
87         options.backdrop = 'static';
88
89         this.modalRef = this.modalService.open(this.dialogContent, options);
90         DialogComponent.instances[this.identifier] = this;
91
92         if (this.onOpen$) {
93             // Let the digest cycle complete
94             setTimeout(() => this.onOpen$.emit(true));
95         }
96
97         return new Observable(observer => {
98             this.observer = observer;
99
100             this.modalRef.result.then(
101                 // Results are relayed to the caller via our observer.
102                 // Our Observer is marked complete via this.close().
103                 // Nothing to do here.
104                 result => {},
105
106                 // Modal was dismissed via UI control which
107                 // bypasses call to this.close()
108                 dismissed => this.finalize()
109             );
110         });
111     }
112
113     // Send a response to the caller without closing the dialog.
114     respond(value: any) {
115         if (this.observer && value !== undefined) {
116             this.observer.next(value);
117         }
118     }
119
120     // Sends error event to the caller and closes the dialog.
121     // Once an error is sent, our observable is complete and
122     // cannot be used again to send any messages.
123     error(value: any, close?: boolean) {
124         if (this.observer) {
125             console.error('Dialog produced error', value);
126             this.observer.error(value);
127             this.observer = null;
128         }
129         if (this.modalRef) { this.modalRef.close(); }
130         this.finalize();
131     }
132
133     // Close the dialog, optionally with a value to relay to the caller.
134     // Calling close() with no value simply dismisses the dialog.
135     close(value?: any) {
136         this.respond(value);
137         if (this.modalRef) { this.modalRef.close(); }
138         this.finalize();
139     }
140
141     dismiss() {
142         console.warn('Dialog.dismiss() is deprecated.  Use close() instead');
143         this.close();
144     }
145
146     // Clean up after closing the dialog.
147     finalize() {
148         if (this.observer) { // null if this.error() called
149             this.observer.complete();
150             this.observer = null;
151         }
152         this.modalRef = null;
153         delete DialogComponent.instances[this.identifier];
154     }
155
156 }
157
158