And the answer ...:
Service :
- _counter is used for each modal name with a unique name.
- comp.instance.close is a property of the internal component to subscribe to EventEmitter.
.
import { Injectable, ViewContainerRef, ReflectiveInjector, ComponentFactoryResolver, ComponentRef, EventEmitter } from '@angular/core'; import { CtmAlertComponent } from './ctmAlert/ctmAlert.component'; @Injectable() export class AlertCtmService { private _vcr: ViewContainerRef; private _counter: number = 0; constructor(private componentFactoryResolver: ComponentFactoryResolver, public viewRef: ViewContainerRef) { console.log("AlertCtmService.constructor:"); //TODO: Consider appending to this.viewRef: "#alertCtmServiceContainer" as a Dom elemnt perent container which will hold all AlertModals: // Maybe by: // this.viewRef.element.nativeElement.insertAdjacentHTML('beforeend', '<div class="alertCtmServiceContainer"></div>'); this._vcr = this.viewRef; } public alertOK(alertMsg: string): EventEmitter<any> { return this.createEventEmitterComponent("CtmAlertComponent", alertMsg, false); } public alertConfirm(alertMsg: string): EventEmitter<any> { return this.createEventEmitterComponent("CtmAlertComponent", alertMsg, true); } private createEventEmitterComponent(componentName: string, alertMsg: string, isConfirm: boolean): EventEmitter<any> { console.log("AlertCtmService.createEventEmitterComponent:"); switch (componentName) { case "CtmAlertComponent": default: var _component = CtmAlertComponent; break; } let factory = this.componentFactoryResolver.resolveComponentFactory(_component); // vCref is needed cause of that injector.. let injector = ReflectiveInjector.fromResolvedProviders([], this._vcr.parentInjector); // create component without adding it directly to the DOM let comp = factory.create(injector); // add inputs first !! otherwise component/template crashes .. comp.instance.close.subscribe(resp => { console.log("AlertCtmService.createEventEmitterComponent: comp.instance.close.subscribe: resp=" + resp.ok); comp.destroy(); }) comp.instance.alertBodyMsg = alertMsg; comp.instance.isConfirm = isConfirm; comp.instance.nameId = "Modal" +(++this._counter).toString(); // all inputs set? add it to the DOM .. this._vcr.insert(comp.hostView); //return null; return comp.instance.close; } public init(vCref: ViewContainerRef): ViewContainerRef { this._vcr = vCref; return this._vcr; } }
- Internal component:
- Using Bootstrap to Handle a Modal Display in an Interface: Modal ('show') \ modal ('hide').
.
import { Component, AfterViewInit, Input, ViewChild, ElementRef, Renderer, NgZone, EventEmitter} from '@angular/core'; @Component({ selector: 'ctm-alert', styles: [``], templateUrl: '/app/shared/alertCtm/ctmAlert/CtmAlert.component.html', styleUrls: ['./app/shared/alertCtm/ctmAlert/CtmAlert.component.css'], providers: [] }) export class CtmAlertComponent implements AfterViewInit { public ModalIsVisible: boolean; //private static subscriptions: Object = {}; //enums = Enums; close = new EventEmitter(); public nameId = ""; private isOk = false; alertBodyMsg: string = ""; isConfirm = false; constructor() { console.log("CtmAlertComponent.constructor:"); } ngAfterViewInit() { this.showModal(); var attrId = this.getIdAttr(); $('#' + attrId).on('hidden.bs.modal', function () { debugger; console.log('CtmAlertComponent: #licenseModal_XXX.on(hidden.bs.modal)'); this.submitStatus(); }.bind(this) ); } showModal() { this.ModalIsVisible = true; var attrId = '#' +this.getIdAttr(); $(attrId).modal('show'); } hideModal() { this.ModalIsVisible = false; var attrId = '#' + this.getIdAttr(); $(attrId).modal('hide'); } getIdAttr(): string { return "ctmAlertModal_" + this.nameId; } submitStatus() { var resp = { ok: (this.isOk == true) }; this.close.emit(resp); } submitOk() { this.isOk = true; this.hideModal(); } submitCancel() { this.isOk = false; this.hideModal(); } }
.
import { CtmAlertComponent } from './shared/alertCtm/ctmAlert/ctmAlert.component'; @NgModule({ imports: [ BrowserModule, HttpModule, AppRoutingModule, ... ], declarations: [ CtmAlertComponent, AppComponent, ... ], entryComponents: [CtmAlertComponent], providers: [ ... ], bootstrap: [AppComponent], }) export class AppModule { } enableProdMode();
Modal interface :
- This html template is based on the bootstrap user interface:
.
<div class="ctmAlertModal modal fade in" [id]="getIdAttr()" role="dialog"> <div class="modal-dialog modal-lg" [ngClass]="{'modal-lg-6': true }"> <div class="modal-content"> <div class="modal-header" style=""> <div class="pull-right" style="position: relative;"> <a href="#" data-dismiss="modal" (click)="hideModal()"><span class="fa fa-times-circle" aria-hidden="true" style="color: #949494"></span></a> </div> </div> <div class="modal-body"> <div class="modal-body-msg"> {{alertBodyMsg}} </div> <div class="modal-body-buttons"> <div style="margin: 0 auto;" [style.width]="(isConfirm)? '165px' : '70px' "> <button type="button" *ngIf="isConfirm" class="btn-submit pull-left btn-cancel" [ngClass]="{'disabled': false }" [disabled]="false" (click)="submitCancel()"> Cancel </button> <button type="button" class="btn-submit pull-right" [ngClass]="{'disabled': false }" [disabled]="false" (click)="submitOk()"> OK </button> </div> </div> </div> </div> </div> </div>
.
.
this.alertCtmService.alertOK("Save changes???").subscribe(function (resp) { console.log("alertCtmService.alertOK.subscribe: resp=" + resp.ok); this.saveData(); }.bind(this) );
**
**
Sources: