diff --git a/src/lib/dialog/dialog.spec.ts b/src/lib/dialog/dialog.spec.ts index e4196924deaa..dab1cfeb3cf7 100644 --- a/src/lib/dialog/dialog.spec.ts +++ b/src/lib/dialog/dialog.spec.ts @@ -9,6 +9,8 @@ import { } from '@angular/core/testing'; import {By} from '@angular/platform-browser'; import {NgModule, Component, Directive, ViewChild, ViewContainerRef, Injector} from '@angular/core'; +import {Location} from '@angular/common'; +import {SpyLocation} from '@angular/common/testing'; import {MdDialogModule} from './index'; import {MdDialog} from './dialog'; import {OverlayContainer} from '../core'; @@ -22,6 +24,7 @@ describe('MdDialog', () => { let testViewContainerRef: ViewContainerRef; let viewContainerFixture: ComponentFixture; + let mockLocation: SpyLocation; beforeEach(async(() => { TestBed.configureTestingModule({ @@ -30,15 +33,17 @@ describe('MdDialog', () => { {provide: OverlayContainer, useFactory: () => { overlayContainerElement = document.createElement('div'); return {getContainerElement: () => overlayContainerElement}; - }} + }}, + {provide: Location, useClass: SpyLocation} ], }); TestBed.compileComponents(); })); - beforeEach(inject([MdDialog], (d: MdDialog) => { + beforeEach(inject([MdDialog, Location], (d: MdDialog, l: Location) => { dialog = d; + mockLocation = l as SpyLocation; })); beforeEach(() => { @@ -271,6 +276,28 @@ describe('MdDialog', () => { expect(overlayContainerElement.querySelectorAll('md-dialog-container').length).toBe(0); }); + it('should close all open dialogs when the user goes forwards/backwards in history', () => { + dialog.open(PizzaMsg); + dialog.open(PizzaMsg); + + expect(overlayContainerElement.querySelectorAll('md-dialog-container').length).toBe(2); + + mockLocation.simulateUrlPop(''); + + expect(overlayContainerElement.querySelectorAll('md-dialog-container').length).toBe(0); + }); + + it('should close all open dialogs when the location hash changes', () => { + dialog.open(PizzaMsg); + dialog.open(PizzaMsg); + + expect(overlayContainerElement.querySelectorAll('md-dialog-container').length).toBe(2); + + mockLocation.simulateHashChange(''); + + expect(overlayContainerElement.querySelectorAll('md-dialog-container').length).toBe(0); + }); + describe('disableClose option', () => { it('should prevent closing via clicks on the backdrop', () => { dialog.open(PizzaMsg, { diff --git a/src/lib/dialog/dialog.ts b/src/lib/dialog/dialog.ts index 93e48cd6b2fc..017d5a38cafc 100644 --- a/src/lib/dialog/dialog.ts +++ b/src/lib/dialog/dialog.ts @@ -1,4 +1,5 @@ import {Injector, ComponentRef, Injectable, Optional, SkipSelf} from '@angular/core'; +import {Location} from '@angular/common'; import {Observable} from 'rxjs/Observable'; import {Subject} from 'rxjs/Subject'; @@ -49,7 +50,14 @@ export class MdDialog { constructor( private _overlay: Overlay, private _injector: Injector, - @Optional() @SkipSelf() private _parentDialog: MdDialog) { } + private _location: Location, + @Optional() @SkipSelf() private _parentDialog: MdDialog) { + + // 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`). + _location.subscribe(() => this.closeAll()); + } /** * Opens a modal dialog containing the given component. @@ -77,12 +85,14 @@ export class MdDialog { closeAll(): void { let i = this._openDialogs.length; - while (i--) { - // The `_openDialogs` property isn't updated after close until the rxjs subscription - // runs on the next microtask, in addition to modifying the array as we're going - // through it. We loop through all of them and call close without assuming that - // they'll be removed from the list instantaneously. - this._openDialogs[i].close(); + if (i > 0) { + while (i--) { + // The `_openDialogs` property isn't updated after close until the rxjs subscription + // runs on the next microtask, in addition to modifying the array as we're going + // through it. We loop through all of them and call close without assuming that + // they'll be removed from the list instantaneously. + this._openDialogs[i].close(); + } } } @@ -127,7 +137,7 @@ export class MdDialog { 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); + let dialogRef = new MdDialogRef(overlayRef) as MdDialogRef; if (!dialogContainer.dialogConfig.disableClose) { // When the dialog backdrop is clicked, we want to close it. diff --git a/src/lib/dialog/index.ts b/src/lib/dialog/index.ts index e14bc904b78b..bc2e0cbfe0d4 100644 --- a/src/lib/dialog/index.ts +++ b/src/lib/dialog/index.ts @@ -1,4 +1,5 @@ import {NgModule, ModuleWithProviders} from '@angular/core'; +import {CommonModule} from '@angular/common'; import { OverlayModule, PortalModule, @@ -17,6 +18,7 @@ import { @NgModule({ imports: [ + CommonModule, OverlayModule, PortalModule, A11yModule,