Created
March 9, 2016 18:24
-
-
Save baio/51576eb0dffdeb624a97 to your computer and use it in GitHub Desktop.
Better modal
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
///<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]}) | |
]); | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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">×</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); | |
} | |
} | |
////////////////////////////////// |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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