Skip to content
This repository has been archived by the owner on Sep 7, 2020. It is now read-only.

Commit

Permalink
Abstract to ModalInstance. Use observables. Distinguish close and dis…
Browse files Browse the repository at this point in the history
…miss events.
  • Loading branch information
Douglas Ludlow committed Mar 23, 2016
1 parent 03c7431 commit 9fd9592
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 92 deletions.
6 changes: 3 additions & 3 deletions demo/modal-demo.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ <h1>Modal</h1>
<button type="button" class="btn btn-default" (click)="navigateModal.open()">Navigation Modal</button>
<div [hidden]="!selected">Selection from a modal: {{ selected }}</div>

<modal [animation]="animationsEnabled" (onClose)="onClose($event)" #modal>
<modal [animation]="animationsEnabled" (onClose)="closed()" (onDismiss)="dismissed()" #modal>
<modal-header [show-close]="true">
<h4 class="modal-title">I'm a modal!</h4>
</modal-header>
Expand All @@ -25,7 +25,7 @@ <h4 class="modal-title">I'm a modal!</h4>
<modal-footer [show-default-buttons]="true"></modal-footer>
</modal>

<modal [animation]="animationsEnabled" [keyboard]="false" [backdrop]="'static'" (onClose)="onClose($event)" #noKeyboardModal>
<modal [animation]="animationsEnabled" [keyboard]="false" [backdrop]="'static'" (onClose)="onClose()" #noKeyboardModal>
<modal-header [show-close]="false">
<h4 class="modal-title">Can't close me with your keyboard!</h4>
</modal-header>
Expand All @@ -40,7 +40,7 @@ <h4 class="modal-title">Can't close me with your keyboard!</h4>
<modal-footer [show-default-buttons]="true"></modal-footer>
</modal>

<modal [animation]="animationsEnabled" (onClose)="navigate($event)" #navigateModal>
<modal [animation]="animationsEnabled" (onClose)="navigate()" #navigateModal>
<modal-header [show-close]="true">
<h4 class="modal-title">I'm a modal!</h4>
</modal-header>
Expand Down
14 changes: 7 additions & 7 deletions demo/modal-demo.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ export class ModalDemoComponent {

constructor(private router: Router) { }

onClose(result: ModalResult) {
if (result === ModalResult.Close) {
this.selected = this.modalSelected;
}
closed() {
this.selected = '(closed) ' + this.modalSelected;
}

dismissed() {
this.selected = '(dismissed)';
}

navigate(result: ModalResult) {
if (result === ModalResult.Close) {
this.router.navigateByUrl('/hello');
}
this.router.navigateByUrl('/hello');
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ng2-bs3-modal",
"version": "0.3.1",
"version": "0.4.0",
"description": "Angular2 Boostrap3 Modal Component",
"main": "ng2-bs3-modal.js",
"scripts": {
Expand Down
98 changes: 98 additions & 0 deletions src/ng2-bs3-modal/components/modal-instance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { ElementRef } from 'angular2/core';
import { Observable } from 'rxjs/Observable';
import { Subscriber } from 'rxjs/Subscriber';
import 'rxjs/add/operator/toPromise';

declare var jQuery: any;

export class ModalInstance {

private suffix: string = 'ng2-bs3-modal';
private shownEventName: string = 'shown.bs.modal';
private hiddenEventName: string = 'hidden.bs.modal';

$modal: any;
shown: Observable<any>;
shownSubscriber: Subscriber<any>;
hidden: Observable<ModalResult>;
hiddenSubscriber: Subscriber<ModalResult>;
result: ModalResult;
visible: boolean = false;

constructor(private element: ElementRef) {
this.init();
}

open(): Promise<any> {
this.create();
return this.show();
}

close(): Promise<any> {
this.result = ModalResult.Close;
return this.hide();
}

dismiss(): Promise<any> {
this.result = ModalResult.Dismiss;
return this.hide();
}

destroy(): Promise<any> {
return this.hide().then(() => {
this.$modal.data('bs.modal', null);
this.$modal.remove();
});
}

private show() {
this.$modal.appendTo('body');
this.$modal.modal('show');
return this.shown.toPromise();
}

private hide(): Promise<ModalResult> {
if (this.$modal) this.$modal.modal('hide');
return this.hidden.toPromise();
}

private init() {
this.shown = new Observable<any>((subscriber: Subscriber<any>) => {
this.shownSubscriber = subscriber;
});

this.hidden = new Observable<ModalResult>((subscriber: Subscriber<ModalResult>) => {
this.hiddenSubscriber = subscriber;
});
}

private create() {
if (!this.$modal) {
this.$modal = jQuery(this.element.nativeElement.firstElementChild);
this.$modal.appendTo('body').modal({ show: false });
}

this.$modal
.off(`${this.shownEventName}.${this.suffix}`)
.on(`${this.shownEventName}.${this.suffix}`, () => {
this.visible = true;
this.shownSubscriber.next();
this.shownSubscriber.complete();
})
.off(`${this.hiddenEventName}.${this.suffix}`)
.on(`${this.hiddenEventName}.${this.suffix}`, () => {
this.visible = false;
if (this.result === ModalResult.None) {
this.result = ModalResult.Dismiss;
};
this.hiddenSubscriber.next(this.result);
this.hiddenSubscriber.complete();
});
}
}

export enum ModalResult {
None,
Close,
Dismiss
}
105 changes: 24 additions & 81 deletions src/ng2-bs3-modal/components/modal.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Component, AfterViewInit, OnDestroy, Input, Output, EventEmitter, Type } from 'angular2/core';

declare var jQuery: any;
import { Component, AfterViewInit, OnDestroy, Input, Output, EventEmitter, Type, ElementRef } from 'angular2/core';
import { CanDeactivate, ComponentInstruction } from 'angular2/router';
import { ModalInstance, ModalResult } from './modal-instance';

@Component({
selector: 'modal',
template: `
<div id="{{id}}" class="modal" [ngClass]="{ fade: animation }" tabindex="-1" role="dialog"
<div class="modal" [ngClass]="{ fade: animation }" tabindex="-1" role="dialog"
[attr.data-keyboard]="keyboard" [attr.data-backdrop]="backdrop">
<div class="modal-dialog" [ngClass]="{ 'modal-sm': isSmall(), 'modal-lg': isLarge() }">
<div class="modal-content">
Expand All @@ -15,97 +15,51 @@ declare var jQuery: any;
</div>
`
})
export class ModalComponent implements OnDestroy {
export class ModalComponent implements OnDestroy, CanDeactivate {

id: string = uniqueId('modal_');
$modal: any;
result: ModalResult = ModalResult.None;
hiding: boolean = false;
instance: ModalInstance;
overrideSize: string = null;
visible: boolean = false;
@Input() animation: boolean = true;
@Input() backdrop: string;
@Input() keyboard: boolean;
@Input() size: string;
@Output() onClose: EventEmitter<ModalResult> = new EventEmitter(false);

init() {
this.$modal = jQuery('#' + this.id);
this.$modal.appendTo('body').modal({ show: false });
this.$modal
.off('shown.bs.modal.ng2-bs3-modal')
.on('shown.bs.modal.ng2-bs3-modal', (e) => {
this.visible = true;
})
.off('hide.bs.modal.ng2-bs3-modal')
.on('hide.bs.modal.ng2-bs3-modal', (e) => {
this.hiding = true;
if (this.result === ModalResult.None) this.dismiss();
this.result = ModalResult.None;
})
.off('hidden.bs.modal.ng2-bs3-modal')
.on('hidden.bs.modal.ng2-bs3-modal', (e) => {
this.hiding = false;
this.overrideSize = null;
this.visible = false;
});
@Output() onClose: EventEmitter<any> = new EventEmitter(false);
@Output() onDismiss: EventEmitter<any> = new EventEmitter(false);

constructor(el: ElementRef) {
this.instance = new ModalInstance(el);
this.instance.hidden.subscribe((result) => {
if (result = ModalResult.Dismiss)
this.onDismiss.emit(undefined);
});
}

ngOnDestroy() {
if (this.$modal) {
if (this.visible) {
this.$modal.one('hidden.bs.modal', () => {
this.destroy();
});
this.$modal.hide();
}
else {
this.destroy();
}
}
this.instance.destroy();
}

private destroy() {
this.$modal.data('bs.modal', null);
this.$modal.remove();
routerCanDeactivate(next: ComponentInstruction, prev: ComponentInstruction): any {
return this.instance.destroy();
}

open(size?: string) {
return new Promise((resolve, reject) => {
this.init();
if (ModalSize.validSize(size)) this.overrideSize = size;
this.$modal.one('shown.bs.modal', () => {
resolve();
});
this.$modal.modal('show');
});
if (ModalSize.validSize(size)) this.overrideSize = size;
return this.instance.open();
}

close() {
return new Promise((resolve, reject) => {
this.result = ModalResult.Close;
this.onClose.emit(this.result);
this.hide(resolve);
return this.instance.close().then(() => {
this.onClose.emit(undefined);
});
}

dismiss() {
return new Promise((resolve, reject) => {
this.result = ModalResult.Dismiss;
this.onClose.emit(this.result);
this.hide(resolve);
return this.instance.dismiss().then(() => {
this.onDismiss.emit(undefined);
});
}

private hide(resolve) {
if (!this.hiding) {
this.$modal.one('hidden.bs.modal', () => {
resolve();
});
this.$modal.modal('hide');
}
}

private isSmall() {
return this.overrideSize !== ModalSize.Large && this.size === ModalSize.Small || this.overrideSize === ModalSize.Small;
}
Expand All @@ -123,14 +77,3 @@ export class ModalSize {
return size && (size === ModalSize.Small || size === ModalSize.Large);
}
}

export enum ModalResult {
None,
Close,
Dismiss
}

let id: number = 0;
export function uniqueId(prefix: string): string {
return prefix + ++id;
}
1 change: 1 addition & 0 deletions src/ng2-bs3-modal/ng2-bs3-modal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export * from './components/modal';
export * from './components/modal-header';
export * from './components/modal-body';
export * from './components/modal-footer';
export * from './components/modal-instance';

export const MODAL_DIRECTIVES: Type[] = [
ModalComponent,
Expand Down

0 comments on commit 9fd9592

Please sign in to comment.