From 6506b10fd91a3a291716cb804588cdf65d64652d Mon Sep 17 00:00:00 2001 From: crisbeto Date: Mon, 5 Jun 2017 00:58:15 +0200 Subject: [PATCH] feat(dialog): allow disableClose option to be updated * Exposes the disableClose option via the `MdDialogRef` and allows for it to be updated. * Makes the `containerInstance` private to `MdDialogRef` since it doesn't make sense for it to be public anymore. * Completes the `backdropClick` observable once the associated `overlayRef` is destroyed. This avoids having to unsubscribe manually or having to use `Observable.first`. Fixes #3938. --- src/lib/core/overlay/overlay-ref.ts | 1 + src/lib/core/overlay/overlay.spec.ts | 15 +++++++++++++++ src/lib/dialog/dialog-container.ts | 4 ++-- src/lib/dialog/dialog-ref.ts | 5 ++++- src/lib/dialog/dialog.spec.ts | 22 ++++++++++++++++++++++ src/lib/dialog/dialog.ts | 17 ++++++++++------- 6 files changed, 54 insertions(+), 10 deletions(-) diff --git a/src/lib/core/overlay/overlay-ref.ts b/src/lib/core/overlay/overlay-ref.ts index 6a58b6a2b467..0402d96a0d62 100644 --- a/src/lib/core/overlay/overlay-ref.ts +++ b/src/lib/core/overlay/overlay-ref.ts @@ -91,6 +91,7 @@ export class OverlayRef implements PortalHost { this._detachments.next(); this._detachments.complete(); this._attachments.complete(); + this._backdropClick.complete(); } /** diff --git a/src/lib/core/overlay/overlay.spec.ts b/src/lib/core/overlay/overlay.spec.ts index ccc1424a563a..fcc89334a602 100644 --- a/src/lib/core/overlay/overlay.spec.ts +++ b/src/lib/core/overlay/overlay.spec.ts @@ -284,6 +284,21 @@ describe('Overlay', () => { expect(backdropClickHandler).toHaveBeenCalled(); }); + it('should complete the backdrop click stream once the overlay is destroyed', () => { + let overlayRef = overlay.create(config); + + overlayRef.attach(componentPortal); + viewContainerFixture.detectChanges(); + + let backdrop = overlayContainerElement.querySelector('.cdk-overlay-backdrop') as HTMLElement; + let completeHandler = jasmine.createSpy('backdrop complete handler'); + + overlayRef.backdropClick().subscribe(null, null, completeHandler); + overlayRef.dispose(); + + expect(completeHandler).toHaveBeenCalled(); + }); + it('should apply the default overlay backdrop class', () => { let overlayRef = overlay.create(config); overlayRef.attach(componentPortal); diff --git a/src/lib/dialog/dialog-container.ts b/src/lib/dialog/dialog-container.ts index 279de03cb68a..16d898a36646 100644 --- a/src/lib/dialog/dialog-container.ts +++ b/src/lib/dialog/dialog-container.ts @@ -53,7 +53,7 @@ export function throwMdDialogContentAlreadyAttachedError() { ], host: { '[class.mat-dialog-container]': 'true', - '[attr.role]': 'dialogConfig?.role', + '[attr.role]': 'config?.role', '[@slideDialog]': '_state', '(@slideDialog.done)': '_onAnimationDone($event)', }, @@ -72,7 +72,7 @@ export class MdDialogContainer extends BasePortalHost { private _document: Document; /** The dialog configuration. */ - dialogConfig: MdDialogConfig; + config: MdDialogConfig; /** State of the dialog animation. */ _state: 'void' | 'enter' | 'exit' = 'enter'; diff --git a/src/lib/dialog/dialog-ref.ts b/src/lib/dialog/dialog-ref.ts index 7a9c4a9890f8..3baea263cae6 100644 --- a/src/lib/dialog/dialog-ref.ts +++ b/src/lib/dialog/dialog-ref.ts @@ -18,13 +18,16 @@ export class MdDialogRef { /** The instance of component opened into the dialog. */ componentInstance: T; + /** Whether the user is allowed to close the dialog. */ + disableClose: boolean = this._containerInstance.config.disableClose; + /** Subject for notifying the user that the dialog has finished closing. */ private _afterClosed: Subject = new Subject(); /** Result to be passed to afterClosed. */ private _result: any; - constructor(private _overlayRef: OverlayRef, public _containerInstance: MdDialogContainer) { + constructor(private _overlayRef: OverlayRef, private _containerInstance: MdDialogContainer) { _containerInstance._onAnimationStateChange .filter((event: AnimationEvent) => event.toState === 'exit') .subscribe(() => { diff --git a/src/lib/dialog/dialog.spec.ts b/src/lib/dialog/dialog.spec.ts index 38b42798db3d..fb232549bbee 100644 --- a/src/lib/dialog/dialog.spec.ts +++ b/src/lib/dialog/dialog.spec.ts @@ -429,6 +429,28 @@ describe('MdDialog', () => { expect(overlayContainerElement.querySelector('md-dialog-container')).toBeTruthy(); }); + + it('should allow for the disableClose option to be updated while open', async(() => { + let dialogRef = dialog.open(PizzaMsg, { + disableClose: true, + viewContainerRef: testViewContainerRef + }); + + viewContainerFixture.detectChanges(); + + let backdrop = overlayContainerElement.querySelector('.cdk-overlay-backdrop') as HTMLElement; + backdrop.click(); + + expect(overlayContainerElement.querySelector('md-dialog-container')).toBeTruthy(); + + dialogRef.disableClose = false; + backdrop.click(); + + viewContainerFixture.detectChanges(); + viewContainerFixture.whenStable().then(() => { + expect(overlayContainerElement.querySelector('md-dialog-container')).toBeFalsy(); + }); + })); }); describe('hasBackdrop option', () => { diff --git a/src/lib/dialog/dialog.ts b/src/lib/dialog/dialog.ts index 71bf25ace0c9..35803f2e3f16 100644 --- a/src/lib/dialog/dialog.ts +++ b/src/lib/dialog/dialog.ts @@ -12,7 +12,6 @@ import {MdDialogContainer} from './dialog-container'; import {TemplatePortal} from '../core/portal/portal'; import {BlockScrollStrategy} from '../core/overlay/scroll/block-scroll-strategy'; import {ViewportRuler} from '../core/overlay/position/viewport-ruler'; -import 'rxjs/add/operator/first'; /** @@ -143,7 +142,7 @@ export class MdDialog { let containerPortal = new ComponentPortal(MdDialogContainer, viewContainer); let containerRef: ComponentRef = overlay.attach(containerPortal); - containerRef.instance.dialogConfig = config; + containerRef.instance.config = config; return containerRef.instance; } @@ -162,14 +161,18 @@ export class MdDialog { dialogContainer: MdDialogContainer, overlayRef: OverlayRef, config: MdDialogConfig): MdDialogRef { + // Create a reference to the dialog we're creating in order to give the user a handle // to modify and close it. - let dialogRef = new MdDialogRef(overlayRef, dialogContainer); - if (!config.disableClose) { - // When the dialog backdrop is clicked, we want to close it. - overlayRef.backdropClick().first().subscribe(() => dialogRef.close()); + // When the dialog backdrop is clicked, we want to close it. + if (config.hasBackdrop) { + overlayRef.backdropClick().subscribe(() => { + if (!dialogRef.disableClose) { + dialogRef.close(); + } + }); } // We create an injector specifically for the component we're instantiating so that it can @@ -217,7 +220,7 @@ export class MdDialog { */ private _handleKeydown(event: KeyboardEvent): void { let topDialog = this._openDialogs[this._openDialogs.length - 1]; - let canClose = topDialog ? !topDialog._containerInstance.dialogConfig.disableClose : false; + let canClose = topDialog ? !topDialog.disableClose : false; if (event.keyCode === ESCAPE && canClose) { topDialog.close();