Skip to content

Commit 19131ab

Browse files
committed
fix(dialog): close all dialogs on popstate/hashchange
Closes all of the open dialogs when the user goes forwards/backwards in history. Fixes #2601.
1 parent 592f33f commit 19131ab

File tree

3 files changed

+49
-10
lines changed

3 files changed

+49
-10
lines changed

src/lib/dialog/dialog.spec.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import {
99
} from '@angular/core/testing';
1010
import {By} from '@angular/platform-browser';
1111
import {NgModule, Component, Directive, ViewChild, ViewContainerRef, Injector} from '@angular/core';
12+
import {Location} from '@angular/common';
13+
import {SpyLocation} from '@angular/common/testing';
1214
import {MdDialogModule} from './index';
1315
import {MdDialog} from './dialog';
1416
import {OverlayContainer} from '../core';
@@ -22,6 +24,7 @@ describe('MdDialog', () => {
2224

2325
let testViewContainerRef: ViewContainerRef;
2426
let viewContainerFixture: ComponentFixture<ComponentWithChildViewContainer>;
27+
let mockLocation: SpyLocation;
2528

2629
beforeEach(async(() => {
2730
TestBed.configureTestingModule({
@@ -30,15 +33,17 @@ describe('MdDialog', () => {
3033
{provide: OverlayContainer, useFactory: () => {
3134
overlayContainerElement = document.createElement('div');
3235
return {getContainerElement: () => overlayContainerElement};
33-
}}
36+
}},
37+
{provide: Location, useClass: SpyLocation}
3438
],
3539
});
3640

3741
TestBed.compileComponents();
3842
}));
3943

40-
beforeEach(inject([MdDialog], (d: MdDialog) => {
44+
beforeEach(inject([MdDialog, Location], (d: MdDialog, l: Location) => {
4145
dialog = d;
46+
mockLocation = l as SpyLocation;
4247
}));
4348

4449
beforeEach(() => {
@@ -242,6 +247,28 @@ describe('MdDialog', () => {
242247
expect(overlayContainerElement.querySelectorAll('md-dialog-container').length).toBe(0);
243248
});
244249

250+
it('should close all open dialogs when the user goes forwards/backwards in history', () => {
251+
dialog.open(PizzaMsg);
252+
dialog.open(PizzaMsg);
253+
254+
expect(overlayContainerElement.querySelectorAll('md-dialog-container').length).toBe(2);
255+
256+
mockLocation.simulateUrlPop('');
257+
258+
expect(overlayContainerElement.querySelectorAll('md-dialog-container').length).toBe(0);
259+
});
260+
261+
it('should close all open dialogs when the location hash changes', () => {
262+
dialog.open(PizzaMsg);
263+
dialog.open(PizzaMsg);
264+
265+
expect(overlayContainerElement.querySelectorAll('md-dialog-container').length).toBe(2);
266+
267+
mockLocation.simulateHashChange('');
268+
269+
expect(overlayContainerElement.querySelectorAll('md-dialog-container').length).toBe(0);
270+
});
271+
245272
describe('disableClose option', () => {
246273
it('should prevent closing via clicks on the backdrop', () => {
247274
dialog.open(PizzaMsg, {

src/lib/dialog/dialog.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {Injector, ComponentRef, Injectable, Optional, SkipSelf} from '@angular/core';
2+
import {Location} from '@angular/common';
23

34
import {Overlay, OverlayRef, ComponentType, OverlayState, ComponentPortal} from '../core';
45
import {extendObject} from '../core/util/object-extend';
@@ -29,7 +30,14 @@ export class MdDialog {
2930
constructor(
3031
private _overlay: Overlay,
3132
private _injector: Injector,
32-
@Optional() @SkipSelf() private _parentDialog: MdDialog) { }
33+
private _location: Location,
34+
@Optional() @SkipSelf() private _parentDialog: MdDialog) {
35+
36+
// Close all of the dialogs when the user goes forwards/backwards in history or when the
37+
// location hash changes. Note that this usually doesn't include clicking on links (unless
38+
// the user is using the `HashLocationStrategy`).
39+
_location.subscribe(() => this.closeAll());
40+
}
3341

3442
/**
3543
* Opens a modal dialog containing the given component.
@@ -56,12 +64,14 @@ export class MdDialog {
5664
closeAll(): void {
5765
let i = this._openDialogs.length;
5866

59-
while (i--) {
60-
// The `_openDialogs` property isn't updated after close until the rxjs subscription
61-
// runs on the next microtask, in addition to modifying the array as we're going
62-
// through it. We loop through all of them and call close without assuming that
63-
// they'll be removed from the list instantaneously.
64-
this._openDialogs[i].close();
67+
if (i > 0) {
68+
while (i--) {
69+
// The `_openDialogs` property isn't updated after close until the rxjs subscription
70+
// runs on the next microtask, in addition to modifying the array as we're going
71+
// through it. We loop through all of them and call close without assuming that
72+
// they'll be removed from the list instantaneously.
73+
this._openDialogs[i].close();
74+
}
6575
}
6676
}
6777

@@ -106,7 +116,7 @@ export class MdDialog {
106116
config?: MdDialogConfig): MdDialogRef<T> {
107117
// Create a reference to the dialog we're creating in order to give the user a handle
108118
// to modify and close it.
109-
let dialogRef = <MdDialogRef<T>> new MdDialogRef(overlayRef);
119+
let dialogRef = new MdDialogRef(overlayRef) as MdDialogRef<T>;
110120

111121
if (!dialogContainer.dialogConfig.disableClose) {
112122
// When the dialog backdrop is clicked, we want to close it.

src/lib/dialog/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {NgModule, ModuleWithProviders} from '@angular/core';
2+
import {CommonModule} from '@angular/common';
23
import {
34
OverlayModule,
45
PortalModule,
@@ -17,6 +18,7 @@ import {
1718

1819
@NgModule({
1920
imports: [
21+
CommonModule,
2022
OverlayModule,
2123
PortalModule,
2224
A11yModule,

0 commit comments

Comments
 (0)