The Problem
Angular Material dialogs use the CDK Overlay system. When a dialog does not close properly, the overlay backdrop remains on screen, blocking all user interaction.
Symptoms
- dialog.close() called but dialog remains visible
- Backdrop overlay blocks all clicks
- Multiple dialogs stack on top of each other
- Browser back button does not close dialog
Real Error Scenario
```typescript // WRONG: Closing before async operation completes constructor(private dialog: MatDialog) {}
async saveAndClose() { await this.saveData(); this.dialogRef.close(); // May fail if called outside NgZone }
// Or: calling close on wrong reference this.dialog.closeAll(); // Closes ALL dialogs, not just this one ```
How to Fix It
Fix 1: Proper Dialog Close Pattern
```typescript @Component({...}) export class EditUserDialog { constructor( public dialogRef: MatDialogRef<EditUserDialog>, @Inject(MAT_DIALOG_DATA) public data: UserData ) {}
onSave() { this.dialogRef.close({ success: true, data: this.form.value }); }
onCancel() { this.dialogRef.close(); } }
// Opening component const dialogRef = this.dialog.open(EditUserDialog, { data: userData });
dialogRef.afterClosed().subscribe(result => { if (result?.success) { this.handleSave(result.data); } }); ```
Fix 2: Handle Dialog Close in NgZone
```typescript constructor( private dialogRef: MatDialogRef<EditUserDialog>, private ngZone: NgZone ) {}
onSave() { this.saveService.update(this.form.value).subscribe({ next: () => { this.ngZone.run(() => { this.dialogRef.close({ success: true }); }); }, error: (err) => { this.error = err.message; } }); } ```
Fix 3: Close All Dialogs on Navigation
// In app.component.ts
constructor(private dialog: MatDialog, private router: Router) {
this.router.events.subscribe(event => {
if (event instanceof NavigationStart) {
this.dialog.closeAll();
}
});
}Fix 4: Handle Escape Key and Backdrop Click
```typescript const dialogRef = this.dialog.open(EditUserDialog, { data: userData, disableClose: false, // Allow closing with Escape and backdrop click hasBackdrop: true, backdropClass: 'custom-backdrop' });
// Prevent closing during save this.isSaving = true; dialogRef.disableClose = true;
this.saveService.update(data).subscribe({ next: () => { dialogRef.disableClose = false; dialogRef.close(); } }); ```
Fix 5: Force Close Overlay
// Last resort: directly access overlay
this.dialogRef.componentInstance.dialogRef.close();
// Or via OverlayRef
this.dialog.getDialogs().forEach(ref => ref.close());