From 23ab15256fc9b0ebd014872aea96c8639f41b7cb Mon Sep 17 00:00:00 2001 From: Thomas Pink Date: Fri, 27 Jan 2017 00:00:05 +0100 Subject: [PATCH] feat(dialog): add events (observables) for open & closeAll (#2522) * added afterOpen & afterAllClosed observables to NgDialog + tests + demo * typo * fixed demo for aot * removed unused src argument in dialog demo * added viewContainerRef to dialog open tests * reworked dialog closeAll & open events based on feedback * added comment to change commit to the right username * added types for afterOpen & afterAllClosed * _afterAllClosed and _afterOpen subjects now delegate to state of parentDialog & removed spaces from inside import braces * fixed tslint error in dialog.ts --- src/demo-app/demo-app-module.ts | 4 ++- src/demo-app/dialog/dialog-demo.ts | 54 ++++++++++++++++++++++++++++-- src/lib/dialog/dialog.spec.ts | 29 ++++++++++++++++ src/lib/dialog/dialog.ts | 26 ++++++++++++++ 4 files changed, 110 insertions(+), 3 deletions(-) diff --git a/src/demo-app/demo-app-module.ts b/src/demo-app/demo-app-module.ts index 47e20162af9e..47b54dbb1036 100644 --- a/src/demo-app/demo-app-module.ts +++ b/src/demo-app/demo-app-module.ts @@ -8,7 +8,7 @@ import {MaterialModule, OverlayContainer, FullscreenOverlayContainer} from '@angular/material'; import {DEMO_APP_ROUTES} from './demo-app/routes'; import {ProgressBarDemo} from './progress-bar/progress-bar-demo'; -import {JazzDialog, ContentElementDialog, DialogDemo} from './dialog/dialog-demo'; +import {JazzDialog, ContentElementDialog, DialogDemo, IFrameDialog} from './dialog/dialog-demo'; import {RippleDemo} from './ripple/ripple-demo'; import {IconDemo} from './icon/icon-demo'; import {GesturesDemo} from './gestures/gestures-demo'; @@ -65,6 +65,7 @@ import {InputContainerDemo} from './input/input-container-demo'; InputContainerDemo, JazzDialog, ContentElementDialog, + IFrameDialog, ListDemo, LiveAnnouncerDemo, MdCheckboxDemoNestedChecklist, @@ -100,6 +101,7 @@ import {InputContainerDemo} from './input/input-container-demo'; DemoApp, JazzDialog, ContentElementDialog, + IFrameDialog, RotiniPanel, ScienceJoke, SpagettiPanel, diff --git a/src/demo-app/dialog/dialog-demo.ts b/src/demo-app/dialog/dialog-demo.ts index 1efe194bd31d..5e91c097f9aa 100644 --- a/src/demo-app/dialog/dialog-demo.ts +++ b/src/demo-app/dialog/dialog-demo.ts @@ -1,4 +1,5 @@ -import {Component} from '@angular/core'; +import {Component, Inject} from '@angular/core'; +import {DOCUMENT} from '@angular/platform-browser'; import {MdDialog, MdDialogRef, MdDialogConfig} from '@angular/material'; @Component({ @@ -23,7 +24,19 @@ export class DialogDemo { } }; - constructor(public dialog: MdDialog) { } + constructor(public dialog: MdDialog, @Inject(DOCUMENT) doc: any) { + // Possible useful example for the open and closeAll events. + // Adding a class to the body if a dialog opens and + // removing it after all open dialogs are closed + dialog.afterOpen.subscribe((ref: MdDialogRef) => { + if (!doc.body.classList.contains('no-scroll')) { + doc.body.classList.add('no-scroll'); + } + }); + dialog.afterAllClosed.subscribe(() => { + doc.body.classList.remove('no-scroll'); + }); + } openJazz() { this.dialogRef = this.dialog.open(JazzDialog, this.config); @@ -91,9 +104,46 @@ export class JazzDialog { color="primary" href="https://en.wikipedia.org/wiki/Neptune" target="_blank">Read more on Wikipedia + + ` }) export class ContentElementDialog { actionsAlignment: string; + + constructor(public dialog: MdDialog) { } + + showInStackedDialog() { + this.dialog.open(IFrameDialog); + } +} + +@Component({ + selector: 'demo-iframe-dialog', + styles: [ + `iframe { + width: 800px; + }` + ], + template: ` +

Neptune

+ + + + + + + + + ` +}) +export class IFrameDialog { } diff --git a/src/lib/dialog/dialog.spec.ts b/src/lib/dialog/dialog.spec.ts index 5cc05dd3d70b..e4196924deaa 100644 --- a/src/lib/dialog/dialog.spec.ts +++ b/src/lib/dialog/dialog.spec.ts @@ -150,6 +150,35 @@ describe('MdDialog', () => { expect(overlayContainerElement.querySelector('md-dialog-container')).toBeFalsy(); }); + it('should notify the observers if a dialog has been opened', () => { + let ref: MdDialogRef; + dialog.afterOpen.subscribe(r => { + ref = r; + }); + expect(dialog.open(PizzaMsg, { + viewContainerRef: testViewContainerRef + })).toBe(ref); + }); + + it('should notify the observers if all open dialogs have finished closing', () => { + const ref1 = dialog.open(PizzaMsg, { + viewContainerRef: testViewContainerRef + }); + const ref2 = dialog.open(ContentElementDialog, { + viewContainerRef: testViewContainerRef + }); + let allClosed = false; + + dialog.afterAllClosed.subscribe(() => { + allClosed = true; + }); + + ref1.close(); + expect(allClosed).toBeFalsy(); + ref2.close(); + expect(allClosed).toBeTruthy(); + }); + it('should should override the width of the overlay pane', () => { dialog.open(PizzaMsg, { width: '500px' diff --git a/src/lib/dialog/dialog.ts b/src/lib/dialog/dialog.ts index 17f61bcf7064..93e48cd6b2fc 100644 --- a/src/lib/dialog/dialog.ts +++ b/src/lib/dialog/dialog.ts @@ -1,4 +1,6 @@ import {Injector, ComponentRef, Injectable, Optional, SkipSelf} from '@angular/core'; +import {Observable} from 'rxjs/Observable'; +import {Subject} from 'rxjs/Subject'; import {Overlay, OverlayRef, ComponentType, OverlayState, ComponentPortal} from '../core'; import {extendObject} from '../core/util/object-extend'; @@ -20,12 +22,30 @@ import {MdDialogContainer} from './dialog-container'; @Injectable() export class MdDialog { private _openDialogsAtThisLevel: MdDialogRef[] = []; + private _afterAllClosedAtThisLevel = new Subject(); + private _afterOpenAtThisLevel = new Subject>(); /** Keeps track of the currently-open dialogs. */ get _openDialogs(): MdDialogRef[] { return this._parentDialog ? this._parentDialog._openDialogs : this._openDialogsAtThisLevel; } + /** Subject for notifying the user that all open dialogs have finished closing. */ + get _afterOpen(): Subject> { + return this._parentDialog ? this._parentDialog._afterOpen : this._afterOpenAtThisLevel; + } + /** Subject for notifying the user that a dialog has opened. */ + get _afterAllClosed(): Subject { + return this._parentDialog ? + this._parentDialog._afterAllClosed : this._afterAllClosedAtThisLevel; + } + + /** Gets an observable that is notified when a dialog has been opened. */ + afterOpen: Observable> = this._afterOpen.asObservable(); + + /** Gets an observable that is notified when all open dialog have finished closing. */ + afterAllClosed: Observable = this._afterAllClosed.asObservable(); + constructor( private _overlay: Overlay, private _injector: Injector, @@ -46,6 +66,7 @@ export class MdDialog { this._openDialogs.push(dialogRef); dialogRef.afterClosed().subscribe(() => this._removeOpenDialog(dialogRef)); + this._afterOpen.next(dialogRef); return dialogRef; } @@ -169,6 +190,11 @@ export class MdDialog { if (index > -1) { this._openDialogs.splice(index, 1); + + // no open dialogs are left, call next on afterAllClosed Subject + if (!this._openDialogs.length) { + this._afterAllClosed.next(); + } } } }