7

How Can I close the dialog in this stackblitz example (Minimal, Reproducible Example.) by clicking outside ?

This works fine if I remove the property hasBackdrop: false -> Working Stackblitz Example

dota2pro
  • 7,220
  • 7
  • 44
  • 79

3 Answers3

11

The standard approach for creating an Angular Material Dialog that closes when clicking outside the dialog box (while creating the appearance that there is no backdrop) is to use the built-in library CSS class cdk-overlay-transparent-backdrop and apply it using the MatDialogConfig property backdropClass.

The openDialog method would be:

openDialog(): void {
  const dialogRef = this.dialog.open(DialogOverviewExampleDialog, {
    backdropClass: 'cdk-overlay-transparent-backdrop',
    hasBackdrop: true,
    width: '250px'
  });
}

See Stackblitz Example

Christopher Peisert
  • 21,862
  • 3
  • 86
  • 117
  • 2
    this is same as kevits ' answer . If I do that I have to click twice on a button once to close backdrop once to click the button – dota2pro Sep 11 '19 at 01:29
  • 1
    no i mean if there is button on the page now you have to click on the button twice once to close backdrop second to actually click the button. which is defective behavior – dota2pro Sep 11 '19 at 04:00
  • Hmm.. It doesn't seem to work on outside of router outlet – cyan-kinesin Jun 02 '23 at 16:14
6

In a nutshell, you need to do your own click handling - listen to clicks and determine whether or not they are outside the dialog. This allows you to not trap the click event the way that the backdrop does. Here's a StackBlitz example based on yours:

@Component({
  selector: 'dialog-overview-example',
  templateUrl: 'dialog-overview-example.html',
  styleUrls: ['dialog-overview-example.css'],
})
export class DialogOverviewExample {

  @HostListener('document:click', ['$event'])
  clickout(event) {
    if (this.clickoutHandler) {
      this.clickoutHandler(event);
    }
  }

  animal: string;
  name: string;

  clickoutHandler: Function;

  dialogRef: MatDialogRef<DialogOverviewExampleDialog>;

  constructor(public dialog: MatDialog) {}

  openDialog(): void {
    setTimeout(() => {
      this.dialogRef = this.dialog.open(DialogOverviewExampleDialog, {
        width: '250px',
        hasBackdrop: false
      });

      this.dialogRef.afterOpened().subscribe(() => {
        this.clickoutHandler = this.closeDialogFromClickout;
      });

      this.dialogRef.afterClosed().subscribe(() => {
        this.clickoutHandler = null;
      });
    });
  }

  closeDialogFromClickout(event: MouseEvent) {
    const matDialogContainerEl = this.dialogRef.componentInstance.hostElement.nativeElement.parentElement;
    const rect = matDialogContainerEl.getBoundingClientRect()
    if(event.clientX <= rect.left || event.clientX >= rect.right || 
        event.clientY <= rect.top || event.clientY >= rect.bottom) {
      this.dialogRef.close();
    }
  }
}

@Component({
  selector: 'dialog-overview-example-dialog',
  templateUrl: 'dialog-overview-example-dialog.html',
})
export class DialogOverviewExampleDialog {

  constructor(
    public hostElement: ElementRef,
    public dialogRef: MatDialogRef<DialogOverviewExampleDialog>,
    @Inject(MAT_DIALOG_DATA) public data: DialogData) {
  }

  onNoClick(): void {
    this.dialogRef.close();
  }
}
G. Tranter
  • 16,766
  • 1
  • 48
  • 68
2

There are many ways on how to achieve this. One would be listening on window clicks. Stackblitz

export class DialogOverviewExampleDialog {

  private inited;

  constructor(
    public dialogRef: MatDialogRef<DialogOverviewExampleDialog>,
    @Inject(MAT_DIALOG_DATA) public data: DialogData) { }

  ngOnInit() {
      this.dialogRef.afterOpened().subscribe(() => {
        this.inited = true;
      })
  }

  @HostListener('window:click')
  onNoClick(): void {
    if (this.inited) {
        this.dialogRef.close();
    }
  }

}

But I suggest you edit the css for the backdrop, so it remains invisible but the functionality stays.

.cdk-overlay-dark-backdrop {
    background: transparent;
}
kvetis
  • 6,682
  • 1
  • 28
  • 48
  • 4
    If I do that I have to click twice on a button once to close backdrop once to click the button https://stackblitz.com/edit/angular-tkg6v6-sxgzbz?file=app/dialog-overview-example.ts – dota2pro Sep 10 '19 at 21:38
  • If the backdrop is transparent, it indicates that the other elements are functional, so keeping standard functionality would be rather broken behavior. – DennisK Oct 27 '20 at 13:57