From d917bb41d8f1f449aab9a2b0db919509406a5618 Mon Sep 17 00:00:00 2001 From: crisbeto Date: Sun, 17 Dec 2017 10:49:49 +0100 Subject: [PATCH] feat(dialog): allow for closing on navigation to be disabled Allows consumers to opt out of the functionality that closes all of the open dialogs when going backwards/forwards in history. Fixes #8983. --- src/lib/dialog/dialog-config.ts | 3 +++ src/lib/dialog/dialog-ref.ts | 18 ++++++++++++++++++ src/lib/dialog/dialog.spec.ts | 13 +++++++++++++ src/lib/dialog/dialog.ts | 14 +++----------- 4 files changed, 37 insertions(+), 11 deletions(-) diff --git a/src/lib/dialog/dialog-config.ts b/src/lib/dialog/dialog-config.ts index 79d898938f8c..f965a693e471 100644 --- a/src/lib/dialog/dialog-config.ts +++ b/src/lib/dialog/dialog-config.ts @@ -94,5 +94,8 @@ export class MatDialogConfig { /** Whether the dialog should focus the first focusable element on open. */ autoFocus?: boolean = true; + /** Whether the dialog should close when the user goes backwards/forwards in history. */ + closeOnNavigation?: boolean = true; + // TODO(jelbourn): add configuration for lifecycle hooks, ARIA labelling. } diff --git a/src/lib/dialog/dialog-ref.ts b/src/lib/dialog/dialog-ref.ts index eca66383beb9..164b831475af 100644 --- a/src/lib/dialog/dialog-ref.ts +++ b/src/lib/dialog/dialog-ref.ts @@ -7,11 +7,13 @@ */ import {OverlayRef, GlobalPositionStrategy} from '@angular/cdk/overlay'; +import {Location} from '@angular/common'; import {filter} from 'rxjs/operators/filter'; import {take} from 'rxjs/operators/take'; import {DialogPosition} from './dialog-config'; import {Observable} from 'rxjs/Observable'; import {Subject} from 'rxjs/Subject'; +import {Subscription, ISubscription} from 'rxjs/Subscription'; import {MatDialogContainer} from './dialog-container'; @@ -42,9 +44,13 @@ export class MatDialogRef { /** Result to be passed to afterClosed. */ private _result: R | undefined; + /** Subscription to changes in the user's location. */ + private _locationChanges: ISubscription = Subscription.EMPTY; + constructor( private _overlayRef: OverlayRef, private _containerInstance: MatDialogContainer, + location?: Location, readonly id: string = `mat-dialog-${uniqueId++}`) { // Emit when opening animation completes @@ -64,10 +70,22 @@ export class MatDialogRef { ) .subscribe(() => { this._overlayRef.dispose(); + this._locationChanges.unsubscribe(); this._afterClosed.next(this._result); this._afterClosed.complete(); this.componentInstance = null!; }); + + if (location) { + // Close the dialog when the user goes forwards/backwards in history or when the location + // hash changes. Note that this usually doesn't include clicking on links (unless the user + // is using the `HashLocationStrategy`). + this._locationChanges = location.subscribe(() => { + if (this._containerInstance._config.closeOnNavigation) { + this.close(); + } + }); + } } /** diff --git a/src/lib/dialog/dialog.spec.ts b/src/lib/dialog/dialog.spec.ts index 100ce79b9417..99024c72a77d 100644 --- a/src/lib/dialog/dialog.spec.ts +++ b/src/lib/dialog/dialog.spec.ts @@ -576,6 +576,19 @@ describe('MatDialog', () => { expect(overlayContainerElement.querySelectorAll('mat-dialog-container').length).toBe(0); })); + it('should allow the consumer to disable closing a dialog on navigation', fakeAsync(() => { + dialog.open(PizzaMsg); + dialog.open(PizzaMsg, {closeOnNavigation: false}); + + expect(overlayContainerElement.querySelectorAll('mat-dialog-container').length).toBe(2); + + mockLocation.simulateUrlPop(''); + viewContainerFixture.detectChanges(); + flush(); + + expect(overlayContainerElement.querySelectorAll('mat-dialog-container').length).toBe(1); + })); + it('should have the componentInstance available in the afterClosed callback', fakeAsync(() => { let dialogRef = dialog.open(PizzaMsg); let spy = jasmine.createSpy('afterClosed spy'); diff --git a/src/lib/dialog/dialog.ts b/src/lib/dialog/dialog.ts index ab9c92fe46ce..8fd79b32ec44 100644 --- a/src/lib/dialog/dialog.ts +++ b/src/lib/dialog/dialog.ts @@ -94,17 +94,9 @@ export class MatDialog { constructor( private _overlay: Overlay, private _injector: Injector, - @Optional() location: Location, + @Optional() private _location: Location, @Inject(MAT_DIALOG_SCROLL_STRATEGY) private _scrollStrategy, - @Optional() @SkipSelf() private _parentDialog: MatDialog) { - - // Close all of the dialogs when the user goes forwards/backwards in history or when the - // location hash changes. Note that this usually doesn't include clicking on links (unless - // the user is using the `HashLocationStrategy`). - if (!_parentDialog && location) { - location.subscribe(() => this.closeAll()); - } - } + @Optional() @SkipSelf() private _parentDialog: MatDialog) { } /** * Opens a modal dialog containing the given component. @@ -223,7 +215,7 @@ export class MatDialog { // Create a reference to the dialog we're creating in order to give the user a handle // to modify and close it. - const dialogRef = new MatDialogRef(overlayRef, dialogContainer, config.id); + const dialogRef = new MatDialogRef(overlayRef, dialogContainer, this._location, config.id); // When the dialog backdrop is clicked, we want to close it. if (config.hasBackdrop) {