Skip to content

Instantly share code, notes, and snippets.

@baio
Created March 9, 2016 18:24
Show Gist options
  • Save baio/51576eb0dffdeb624a97 to your computer and use it in GitHub Desktop.
Save baio/51576eb0dffdeb624a97 to your computer and use it in GitHub Desktop.
Better modal
///<reference path="../node_modules/retyped-es6-shim-tsd-ambient/es6-shim.d.ts" />
import {Component, provide, ApplicationRef, DynamicComponentLoader, Injector} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';
import {Observable} from "rxjs/Rx";
import {AppDispatcher} from "./app-dispatcher"
import {AppEP} from "./end-points/app-ep"
import {AppPackage} from "./app-package/app-package"
import {NavBar} from "./nav-bar/nav-bar"
import {Modal} from "./utils/modal"
@Component({
selector: 'app',
template: `
<div>
<div #modalDialog></div>
<div #modalDialogContent></div>
</div>
`
})
export class App {
//activate static Modal references here
constructor(modal: Modal) {
}
}
//Initialize static providers, which is singleton across application here
//All other providers ininitialize in components
bootstrap(App, [
provide(Modal, {useFactory : (appRef: ApplicationRef, dcl: DynamicComponentLoader) => new Modal(dcl, appRef), deps : [ApplicationRef, DynamicComponentLoader]})
]);
import {Component, Input, ChangeDetectionStrategy, EventEmitter, Output} from "angular2/core"
import {Observable} from "rxjs/Rx"
const template = `
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" aria-label="Close" (click)="onClose($event)">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">Modal title</h4>
</div>
<div class="modal-body">
<ng-content #content></ng-content>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
`;
@Component({
selector: "bootstrap-modal",
template: template,
host: {
'tabindex': '0',
'role': 'dialog',
'class': 'in modal',
'style': 'display: block',
'[style.position]': '"absolute"'
},
})
export class BootstrapModal {
@Output() close = new EventEmitter();
constructor() {
}
onClose(event) {
event.stopPropagation();
this.close.emit(undefined);
}
}
//////////////////////////////////
import {DynamicComponentLoader, ApplicationRef, ElementRef, Type, Injectable, ComponentRef, EventEmitter, Injector, provide} from "angular2/core"
import {BootstrapModal} from "./bootstrap-modal"
import {Observable} from "rxjs/Observable"
export interface IModalContent {
close: EventEmitter<any>;
}
@Injectable()
export class ModalInstance {
private _contentRef: ComponentRef;
private _modalRef: ComponentRef;
constructor(private componentLoader: DynamicComponentLoader, private appRef: ApplicationRef) {
}
open(componentType: Type) : Promise<any> {
// TODO: appRef.injector.get(APP_COMPONENT) Doesn't work.
// When it does replace with the hack below.
//let myElementRef = this.appRef.injector.get(APP_COMPONENT).location;
const elementRef: ElementRef = this.appRef['_rootComponents'][0].location;
return new Promise((resolve, reject) =>
this.componentLoader.loadIntoLocation(componentType, elementRef, 'modalDialogContent')
.then(contentRef => {
this._contentRef = contentRef;
return this.componentLoader.loadIntoLocation(BootstrapModal, elementRef, 'modalDialog', null, [[contentRef.location.nativeElement]]);
})
.then(modalRef => {
this._modalRef = modalRef;
})
.then(_ => {
//merge close streams and got only one close event from them, then close & dispose modal
(<IModalContent>this._modalRef.instance).close
.merge((<IModalContent>this._contentRef.instance).close || Observable.never())
.take(1).toPromise()
.then(val => {
this.dispose();
resolve(val);
})
.catch(err => reject(err));
})
);
}
private dispose() {
if (this._contentRef != null) {
this._contentRef.dispose();
this._contentRef = null;
}
if (this._modalRef != null) {
this._modalRef.dispose();
this._modalRef = null;
}
}
}
@Injectable()
export class Modal {
private static componentLoader: DynamicComponentLoader;
private static appRef: ApplicationRef;
constructor(componentLoader: DynamicComponentLoader, appRef: ApplicationRef) {
Modal.componentLoader = componentLoader;
Modal.appRef = appRef;
}
/**
* Type of the component to open as modal content.
* `componentType` could implement `IModalContent`, in this case `Promise` will return `value` and modal will be closed after any `close(value)` event.
* When modal is closed by user `Promise` will return `undefined` value
*/
static open(componentType: Type) : Promise<any> {
var modalInstance = new ModalInstance(Modal.componentLoader, Modal.appRef);
return modalInstance.open(componentType);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment