diff --git a/src/demo-app/dialog/dialog-demo.html b/src/demo-app/dialog/dialog-demo.html
index 45e7ba5925ca..474a59c68718 100644
--- a/src/demo-app/dialog/dialog-demo.html
+++ b/src/demo-app/dialog/dialog-demo.html
@@ -10,6 +10,10 @@
Dialog demo
Open dialog with template content
+
+
Dialog dimensions
@@ -67,6 +71,10 @@ Other options
Last close result: {{lastCloseResult}}
-
+
I'm a template dialog. I've been opened {{numTemplateOpens}} times!
+
+
+ {{ closeAttemptType ? 'Tried to close with: ' + closeAttemptType : 'Have not tried to close yet.' }}
+
diff --git a/src/demo-app/dialog/dialog-demo.ts b/src/demo-app/dialog/dialog-demo.ts
index 5ccd67a0ec68..7958ec00c2f2 100644
--- a/src/demo-app/dialog/dialog-demo.ts
+++ b/src/demo-app/dialog/dialog-demo.ts
@@ -12,6 +12,7 @@ import {MdDialog, MdDialogRef, MdDialogConfig, MD_DIALOG_DATA} from '@angular/ma
export class DialogDemo {
dialogRef: MdDialogRef;
lastCloseResult: string;
+ closeAttemptType: string;
actionsAlignment: string;
config: MdDialogConfig = {
disableClose: false,
@@ -29,7 +30,8 @@ export class DialogDemo {
};
numTemplateOpens = 0;
- @ViewChild(TemplateRef) template: TemplateRef;
+ @ViewChild('templateRef') template: TemplateRef;
+ @ViewChild('disabledCloseRef') templateForDisabledClose: TemplateRef;
constructor(public dialog: MdDialog, @Inject(DOCUMENT) doc: any) {
// Possible useful example for the open and closeAll events.
@@ -63,8 +65,19 @@ export class DialogDemo {
this.numTemplateOpens++;
this.dialog.open(this.template, this.config);
}
-}
+ openWithDisabledClose() {
+ let disableCloseConfig = new MdDialogConfig();
+ disableCloseConfig.disableClose = true;
+
+ let dialogRef = this.dialog.open(this.templateForDisabledClose, disableCloseConfig);
+
+ dialogRef.closeAttempt().subscribe((type) => {
+ this.closeAttemptType = type;
+ });
+
+ }
+}
@Component({
selector: 'demo-jazz-dialog',
diff --git a/src/lib/dialog/dialog-container.ts b/src/lib/dialog/dialog-container.ts
index 1416d36a7bb3..d869942e31ce 100644
--- a/src/lib/dialog/dialog-container.ts
+++ b/src/lib/dialog/dialog-container.ts
@@ -21,12 +21,15 @@ import {BasePortalHost, ComponentPortal, PortalHostDirective, TemplatePortal} fr
import {MdDialogConfig} from './dialog-config';
import {MdDialogContentAlreadyAttachedError} from './dialog-errors';
import {FocusTrapFactory, FocusTrap} from '../core/a11y/focus-trap';
+import {Subject} from 'rxjs/Subject';
import 'rxjs/add/operator/first';
/** Possible states for the dialog container animation. */
export type MdDialogContainerAnimationState = 'void' | 'enter' | 'exit' | 'exit-start';
+/** Possible types of close events */
+export type MdDialogCloseAttempt = 'escape' | 'backdrop';
/**
* Internal component that wraps user-provided dialog content.
@@ -67,6 +70,9 @@ export class MdDialogContainer extends BasePortalHost implements OnDestroy {
/** The dialog configuration. */
dialogConfig: MdDialogConfig;
+ /** subject to expose any close attempt */
+ _closeAttempt = new Subject();
+
/** State of the dialog animation. */
_state: MdDialogContainerAnimationState = 'enter';
diff --git a/src/lib/dialog/dialog-ref.ts b/src/lib/dialog/dialog-ref.ts
index 429dc914c384..0a0bcf713521 100644
--- a/src/lib/dialog/dialog-ref.ts
+++ b/src/lib/dialog/dialog-ref.ts
@@ -2,8 +2,11 @@ import {OverlayRef, GlobalPositionStrategy} from '../core';
import {DialogPosition} from './dialog-config';
import {Observable} from 'rxjs/Observable';
import {Subject} from 'rxjs/Subject';
-import {MdDialogContainer, MdDialogContainerAnimationState} from './dialog-container';
-
+import {
+ MdDialogContainer,
+ MdDialogContainerAnimationState,
+ MdDialogCloseAttempt,
+} from './dialog-container';
// TODO(jelbourn): resizing
// TODO(jelbourn): afterOpen and beforeClose
@@ -53,6 +56,13 @@ export class MdDialogRef {
return this._afterClosed.asObservable();
}
+ /**
+ * Gets an observable that is notified when a close attempt is made
+ */
+ closeAttempt(): Observable {
+ return this._containerInstance._closeAttempt.asObservable();
+ }
+
/**
* Updates the dialog's position.
* @param position New dialog position.
diff --git a/src/lib/dialog/dialog.spec.ts b/src/lib/dialog/dialog.spec.ts
index d9833fe3b53f..0f9e2b2ad8de 100644
--- a/src/lib/dialog/dialog.spec.ts
+++ b/src/lib/dialog/dialog.spec.ts
@@ -157,6 +157,52 @@ describe('MdDialog', () => {
});
}));
+ it('should let the user know that there was a close attempt via the backdrop click', async(() => {
+ const dialogRef = dialog.open(PizzaMsg, {
+ viewContainerRef: testViewContainerRef,
+ disableClose: true
+ });
+
+ let attemptType: any;
+
+ dialogRef.closeAttempt().subscribe((type) => {
+ attemptType = type;
+ });
+
+ viewContainerFixture.detectChanges();
+
+ let backdrop = overlayContainerElement.querySelector('.cdk-overlay-backdrop') as HTMLElement;
+
+ backdrop.click();
+ viewContainerFixture.detectChanges();
+
+ viewContainerFixture.whenStable().then(() => {
+ expect(attemptType).toEqual('backdrop');
+ expect(overlayContainerElement.querySelector('md-dialog-container')).toBeTruthy();
+ });
+ }));
+
+ it('should let the user know that there was a close attempt via the escape key', async(() => {
+ const dialogRef = dialog.open(PizzaMsg, {
+ viewContainerRef: testViewContainerRef,
+ disableClose: true
+ });
+
+ let attemptType: any;
+
+ dialogRef.closeAttempt().subscribe((type) => {
+ attemptType = type;
+ });
+
+ dispatchKeyboardEvent(document, 'keydown', ESCAPE);
+ viewContainerFixture.detectChanges();
+
+ viewContainerFixture.whenStable().then(() => {
+ expect(attemptType).toEqual('escape');
+ expect(overlayContainerElement.querySelector('md-dialog-container')).toBeTruthy();
+ });
+ }));
+
it('should notify the observers if a dialog has been opened', () => {
let ref: MdDialogRef;
dialog.afterOpen.subscribe(r => {
diff --git a/src/lib/dialog/dialog.ts b/src/lib/dialog/dialog.ts
index bc828b62d317..1f21cbd063e8 100644
--- a/src/lib/dialog/dialog.ts
+++ b/src/lib/dialog/dialog.ts
@@ -9,6 +9,7 @@ import {MdDialogConfig} from './dialog-config';
import {MdDialogRef} from './dialog-ref';
import {MdDialogContainer} from './dialog-container';
import {TemplatePortal} from '../core/portal/portal';
+import {Subscription} from 'rxjs/Subscription';
import 'rxjs/add/operator/first';
@@ -21,6 +22,7 @@ export class MdDialog {
private _afterAllClosedAtThisLevel = new Subject();
private _afterOpenAtThisLevel = new Subject>();
private _boundKeydown = this._handleKeydown.bind(this);
+ private _backdropClickSubscription: Subscription;
/** Keeps track of the currently-open dialogs. */
get _openDialogs(): MdDialogRef[] {
@@ -152,6 +154,10 @@ export class MdDialog {
if (!config.disableClose) {
// When the dialog backdrop is clicked, we want to close it.
overlayRef.backdropClick().first().subscribe(() => dialogRef.close());
+ } else {
+ this._backdropClickSubscription = overlayRef.backdropClick().subscribe(() => {
+ dialogContainer._closeAttempt.next('backdrop');
+ });
}
// We create an injector specifically for the component we're instantiating so that it can
@@ -187,6 +193,10 @@ export class MdDialog {
// no open dialogs are left, call next on afterAllClosed Subject
if (!this._openDialogs.length) {
+ if (this._backdropClickSubscription) {
+ this._backdropClickSubscription.unsubscribe();
+ }
+
this._afterAllClosed.next();
document.removeEventListener('keydown', this._boundKeydown);
}
@@ -201,8 +211,11 @@ export class MdDialog {
let topDialog = this._openDialogs[this._openDialogs.length - 1];
let canClose = topDialog ? !topDialog._containerInstance.dialogConfig.disableClose : false;
- if (event.keyCode === ESCAPE && canClose) {
- topDialog.close();
+ if (event.keyCode === ESCAPE && topDialog) {
+ topDialog._containerInstance._closeAttempt.next('escape');
+ if (canClose) {
+ topDialog.close();
+ }
}
}
}