diff --git a/src/material/dialog/dialog-config.ts b/src/material/dialog/dialog-config.ts index 0cfb0ae86234..21b7b2ccaa8a 100644 --- a/src/material/dialog/dialog-config.ts +++ b/src/material/dialog/dialog-config.ts @@ -77,7 +77,7 @@ export class MatDialogConfig { result: Result | undefined, config: Config, componentInstance: Component | null, - ) => boolean; + ) => boolean | Promise; /** Width of the dialog. */ width?: string = ''; diff --git a/src/material/dialog/dialog-ref.ts b/src/material/dialog/dialog-ref.ts index c247fad81cb1..4f4b191c6756 100644 --- a/src/material/dialog/dialog-ref.ts +++ b/src/material/dialog/dialog-ref.ts @@ -123,10 +123,29 @@ export class MatDialogRef { close(dialogResult?: R): void { const closePredicate = this._config.closePredicate; - if (closePredicate && !closePredicate(dialogResult, this._config, this.componentInstance)) { - return; + if (closePredicate) { + const result = closePredicate(dialogResult, this._config, this.componentInstance); + + if (result instanceof Promise) { + result.then(canClose => { + if (canClose) { + this._performClose(dialogResult); + } + }); + return; + } else if (!result) { + return; + } } + this._performClose(dialogResult); + } + + /** + * Performs the actual dialog close operation. + * @param dialogResult Optional result to return to the dialog opener. + */ + private _performClose(dialogResult?: R): void { this._result = dialogResult; // Transition the backdrop in parallel to the dialog. diff --git a/src/material/dialog/dialog.spec.ts b/src/material/dialog/dialog.spec.ts index d68cc19fe351..5d51e6d55aeb 100644 --- a/src/material/dialog/dialog.spec.ts +++ b/src/material/dialog/dialog.spec.ts @@ -1172,6 +1172,50 @@ describe('MatDialog', () => { overlayContainerElement.remove(); })); + + it('should support async closePredicate that returns a Promise', fakeAsync(() => { + let resolvePromise: (value: boolean) => void; + const closedSpy = jasmine.createSpy('closed spy'); + const ref = dialog.open(PizzaMsg, { + closePredicate: () => + new Promise(resolve => { + resolvePromise = resolve; + }), + viewContainerRef: testViewContainerRef, + }); + + ref.afterClosed().subscribe(closedSpy); + viewContainerFixture.detectChanges(); + + expect(getDialogs().length).toBe(1); + + // Try to close - should not close immediately + ref.close('test-result'); + viewContainerFixture.detectChanges(); + flush(); + + expect(getDialogs().length).toBe(1); + expect(closedSpy).not.toHaveBeenCalled(); + + // Resolve promise with false - should still not close + resolvePromise!(false); + tick(); + viewContainerFixture.detectChanges(); + flush(); + + expect(getDialogs().length).toBe(1); + expect(closedSpy).not.toHaveBeenCalled(); + + // Try to close again and resolve with true - should close + ref.close('test-result'); + resolvePromise!(true); + tick(); + viewContainerFixture.detectChanges(); + flush(); + + expect(getDialogs().length).toBe(0); + expect(closedSpy).toHaveBeenCalledWith('test-result'); + })); }); it(