Skip to content

Commit 85912eb

Browse files
committed
feat(material/dialog): support async MatDialogConfig.closePredicate
1 parent d02338b commit 85912eb

3 files changed

Lines changed: 66 additions & 3 deletions

File tree

src/material/dialog/dialog-config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export class MatDialogConfig<D = any> {
7777
result: Result | undefined,
7878
config: Config,
7979
componentInstance: Component | null,
80-
) => boolean;
80+
) => boolean | Promise<boolean>;
8181

8282
/** Width of the dialog. */
8383
width?: string = '';

src/material/dialog/dialog-ref.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,29 @@ export class MatDialogRef<T, R = any> {
123123
close(dialogResult?: R): void {
124124
const closePredicate = this._config.closePredicate;
125125

126-
if (closePredicate && !closePredicate(dialogResult, this._config, this.componentInstance)) {
127-
return;
126+
if (closePredicate) {
127+
const result = closePredicate(dialogResult, this._config, this.componentInstance);
128+
129+
if (result instanceof Promise) {
130+
result.then(canClose => {
131+
if (canClose) {
132+
this._performClose(dialogResult);
133+
}
134+
});
135+
return;
136+
} else if (!result) {
137+
return;
138+
}
128139
}
129140

141+
this._performClose(dialogResult);
142+
}
143+
144+
/**
145+
* Performs the actual dialog close operation.
146+
* @param dialogResult Optional result to return to the dialog opener.
147+
*/
148+
private _performClose(dialogResult?: R): void {
130149
this._result = dialogResult;
131150

132151
// Transition the backdrop in parallel to the dialog.

src/material/dialog/dialog.spec.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1172,6 +1172,50 @@ describe('MatDialog', () => {
11721172

11731173
overlayContainerElement.remove();
11741174
}));
1175+
1176+
it('should support async closePredicate that returns a Promise', fakeAsync(() => {
1177+
let resolvePromise: (value: boolean) => void;
1178+
const closedSpy = jasmine.createSpy('closed spy');
1179+
const ref = dialog.open(PizzaMsg, {
1180+
closePredicate: () =>
1181+
new Promise<boolean>(resolve => {
1182+
resolvePromise = resolve;
1183+
}),
1184+
viewContainerRef: testViewContainerRef,
1185+
});
1186+
1187+
ref.afterClosed().subscribe(closedSpy);
1188+
viewContainerFixture.detectChanges();
1189+
1190+
expect(getDialogs().length).toBe(1);
1191+
1192+
// Try to close - should not close immediately
1193+
ref.close('test-result');
1194+
viewContainerFixture.detectChanges();
1195+
flush();
1196+
1197+
expect(getDialogs().length).toBe(1);
1198+
expect(closedSpy).not.toHaveBeenCalled();
1199+
1200+
// Resolve promise with false - should still not close
1201+
resolvePromise!(false);
1202+
tick();
1203+
viewContainerFixture.detectChanges();
1204+
flush();
1205+
1206+
expect(getDialogs().length).toBe(1);
1207+
expect(closedSpy).not.toHaveBeenCalled();
1208+
1209+
// Try to close again and resolve with true - should close
1210+
ref.close('test-result');
1211+
resolvePromise!(true);
1212+
tick();
1213+
viewContainerFixture.detectChanges();
1214+
flush();
1215+
1216+
expect(getDialogs().length).toBe(0);
1217+
expect(closedSpy).toHaveBeenCalledWith('test-result');
1218+
}));
11751219
});
11761220

11771221
it(

0 commit comments

Comments
 (0)