Skip to content

Commit 5cc58db

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 4b830d3 commit 5cc58db

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
@@ -16,6 +16,8 @@ import {NgModule,
1616
Inject,
1717
} from '@angular/core';
1818
import {By} from '@angular/platform-browser';
19+
import {Location} from '@angular/common';
20+
import {SpyLocation} from '@angular/common/testing';
1921
import {MdDialogModule} from './index';
2022
import {MdDialog} from './dialog';
2123
import {OverlayContainer} from '../core';
@@ -30,6 +32,7 @@ describe('MdDialog', () => {
3032

3133
let testViewContainerRef: ViewContainerRef;
3234
let viewContainerFixture: ComponentFixture<ComponentWithChildViewContainer>;
35+
let mockLocation: SpyLocation;
3336

3437
beforeEach(async(() => {
3538
TestBed.configureTestingModule({
@@ -38,15 +41,17 @@ describe('MdDialog', () => {
3841
{provide: OverlayContainer, useFactory: () => {
3942
overlayContainerElement = document.createElement('div');
4043
return {getContainerElement: () => overlayContainerElement};
41-
}}
44+
}},
45+
{provide: Location, useClass: SpyLocation}
4246
],
4347
});
4448

4549
TestBed.compileComponents();
4650
}));
4751

48-
beforeEach(inject([MdDialog], (d: MdDialog) => {
52+
beforeEach(inject([MdDialog, Location], (d: MdDialog, l: Location) => {
4953
dialog = d;
54+
mockLocation = l as SpyLocation;
5055
}));
5156

5257
beforeEach(() => {
@@ -279,6 +284,28 @@ describe('MdDialog', () => {
279284
expect(overlayContainerElement.querySelectorAll('md-dialog-container').length).toBe(0);
280285
});
281286

287+
it('should close all open dialogs when the user goes forwards/backwards in history', () => {
288+
dialog.open(PizzaMsg);
289+
dialog.open(PizzaMsg);
290+
291+
expect(overlayContainerElement.querySelectorAll('md-dialog-container').length).toBe(2);
292+
293+
mockLocation.simulateUrlPop('');
294+
295+
expect(overlayContainerElement.querySelectorAll('md-dialog-container').length).toBe(0);
296+
});
297+
298+
it('should close all open dialogs when the location hash changes', () => {
299+
dialog.open(PizzaMsg);
300+
dialog.open(PizzaMsg);
301+
302+
expect(overlayContainerElement.querySelectorAll('md-dialog-container').length).toBe(2);
303+
304+
mockLocation.simulateHashChange('');
305+
306+
expect(overlayContainerElement.querySelectorAll('md-dialog-container').length).toBe(0);
307+
});
308+
282309
describe('passing in data', () => {
283310
it('should be able to pass in data', () => {
284311
let config = {

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
import {Observable} from 'rxjs/Observable';
34
import {Subject} from 'rxjs/Subject';
45

@@ -49,7 +50,14 @@ export class MdDialog {
4950
constructor(
5051
private _overlay: Overlay,
5152
private _injector: Injector,
52-
@Optional() @SkipSelf() private _parentDialog: MdDialog) { }
53+
private _location: Location,
54+
@Optional() @SkipSelf() private _parentDialog: MdDialog) {
55+
56+
// Close all of the dialogs when the user goes forwards/backwards in history or when the
57+
// location hash changes. Note that this usually doesn't include clicking on links (unless
58+
// the user is using the `HashLocationStrategy`).
59+
_location.subscribe(() => this.closeAll());
60+
}
5361

5462
/**
5563
* Opens a modal dialog containing the given component.
@@ -77,12 +85,14 @@ export class MdDialog {
7785
closeAll(): void {
7886
let i = this._openDialogs.length;
7987

80-
while (i--) {
81-
// The `_openDialogs` property isn't updated after close until the rxjs subscription
82-
// runs on the next microtask, in addition to modifying the array as we're going
83-
// through it. We loop through all of them and call close without assuming that
84-
// they'll be removed from the list instantaneously.
85-
this._openDialogs[i].close();
88+
if (i > 0) {
89+
while (i--) {
90+
// The `_openDialogs` property isn't updated after close until the rxjs subscription
91+
// runs on the next microtask, in addition to modifying the array as we're going
92+
// through it. We loop through all of them and call close without assuming that
93+
// they'll be removed from the list instantaneously.
94+
this._openDialogs[i].close();
95+
}
8696
}
8797
}
8898

@@ -127,7 +137,7 @@ export class MdDialog {
127137
config?: MdDialogConfig): MdDialogRef<T> {
128138
// Create a reference to the dialog we're creating in order to give the user a handle
129139
// to modify and close it.
130-
let dialogRef = <MdDialogRef<T>> new MdDialogRef(overlayRef);
140+
let dialogRef = new MdDialogRef(overlayRef) as MdDialogRef<T>;
131141

132142
if (!config.disableClose) {
133143
// 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)