5

I am developping an angular application. I want to open a dialog pop up (an instance of MatDialog) when I click on a button. I do it in a method of my main page as the following

    openDialog(event) {
      const element = document.getElementById(event.target.id);
      const jqelement = $(element);
      const position = jqelement.position(); // cache the position
      const bottom = position.top + jqelement.height();
      const dialogConfig = new MatDialogConfig();
      dialogConfig.disableClose = true;
      dialogConfig.autoFocus = true;
      dialogConfig.position = {
        top:  '' + bottom,
        right: '0'
      };
      dialogConfig.width = '50%' ;
      dialogConfig.height = '350px' ;
      console.log(dialogConfig);
      this.dialog.open(UserDialogComponent, dialogConfig);
    }

I want it to be positioned on the right and underneath the button that I click. At the beginning, I put top: 0px so the pop up displayed on the right up corner of the window. It did it well. After two days I tried to position it just below the button (top: 52px), but it does not work, just as if it keep the previous position (during the first two days). Can you help me

PaulBunion
  • 346
  • 2
  • 18
flamant
  • 733
  • 4
  • 15
  • 41
  • Does this answer your question? [How can I position a dialog above the triggering button?](https://stackoverflow.com/questions/43476958/how-can-i-position-a-dialog-above-the-triggering-button) – Christopher Peisert Nov 08 '19 at 20:05
  • Hi Christopher, thank you for your complete answer. I found why in my case, it didn't work. that's because I forgot the unit 'px' : dialogConfig.position = { top: bottom + 'px', right: '0px' }; – flamant Nov 10 '19 at 08:12

2 Answers2

11

The MatDialog popup may be positioned relative to an element. In the example below, the dialog is opened below and to the left of the clicked button based on the button's bounding client rectangle. The element may be referenced via a template reference variable.

Then use MatDialogRef method updatePosition.

Main Component Template

<button #myButton></button>

Main Component

import { AfterViewInit, Component, ElementRef, OnDestroy, ViewChild } from '@angular/core'
import { DialogService } from './dialog.service.ts'

@Component({
  selector: 'main-component',
  templateUrl: 'main.component.html',
  styleUrls: ['main.component.css']
})
export class MainComponent implements AfterViewInit, OnDestroy {
  @ViewChild('myButton', { static: false }) public myButtonRef: ElementRef

  constructor(private dialogService: DialogService) {}

  public openDialog() {
    dialogRef = this.dialogService.openDialog({
      positionRelativeToElement: this.myButtonRef,
      has_backdrop: true
    })

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

dialog.component.ts

import { Component, ElementRef, Inject, OnInit } from '@angular/core'
import { MatDialogConfig, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'


@Component({
  selector: 'dialog-component',
  templateUrl: './dialog.component.html',
  styleUrls: ['./dialog.component.css']
})
export class DialogComponent implements OnInit {
  private positionRelativeToElement: ElementRef

  constructor(public dialogRef: MatDialogRef<DialogComponent>,
    @Inject(MAT_DIALOG_DATA) public options: { positionRelativeToElement: ElementRef }) {

    this.positionRelativeToElement = options.positionRelativeToElement
  }

  ngOnInit() {
    const matDialogConfig = new MatDialogConfig()
    const rect: DOMRect = this.positionRelativeToElement.nativeElement.getBoundingClientRect()

    matDialogConfig.position = { right: `10px`, top: `${rect.bottom + 2}px` }
    this.dialogRef.updatePosition(matDialogConfig.position)
  }
}

dialog.service.ts

import { ElementRef, Injectable } from '@angular/core'
import { MatDialog, MatDialogRef } from '@angular/material'

import { DialogComponent } from './dialog.component'


/**
 * Service to create modal dialog windows.
 */
@Injectable({
  providedIn: 'root'
})
export class DialogService {

  constructor(public dialog: MatDialog) { }

  public openDialog({ position_relative_to_element, user,
    has_backdrop = false, height = '135px', width = '290px' }:
    {
      positionRelativeToElement: ElementRef, hasBackdrop?: boolean,
      height?: string, width?: string
    }): MatDialogRef<DialogComponent> {

    const dialogRef: MatDialogRef<DialogComponent> =
      this.dialog.open(DialogComponent, {
        hasBackdrop: hasBackdrop,
        height: height,
        width: width,
        data: { positionRelativeToElement: positionRelativeToElement }
      })
    return dialogRef
  }
}

Christopher Peisert
  • 21,862
  • 3
  • 86
  • 117
  • Hi Christopher, thank you for your complete answer. I found why in my case, it didn't work. that's because I forgot the unit 'px' : – flamant Nov 09 '19 at 06:26
  • dialogConfig.position = { top: bottom + 'px', right: '0px' }; – flamant Nov 09 '19 at 06:27
0

In addition to Christopher's answer, I'd like to share another method, which uses MatDialogConfig and styling cdk-overlay-panel. I used it to center my dialog relative to a button in a form. My case is specific, because I don't know exact sizes of the dialog to center it horisontally relative to a button. Here is some code:

....
const { bottom, left, right } = this.wrapper.nativeElement.getClientRects()[ 0 ];

const contextMenuDialogRef = this.dialog.open(
  MyDialog,
  {
    position: {
      top: `${ bottom + 24 }px`,
      left: `${ (left + right) / 2 }px`,
    },
    panelClass: 'centered-to-anchor-dialog',
  });

This code positions the dialog top at 24px below wrapper element, and the dialog left at the middle of the wrapper element. panelClass is applied to cdk-overlay-pane material DOM element. To finish the positioning, I add this rule in my src/styles.scss file (the file where we import our material theme):

.centered-to-anchor-dialog {
  .mat-dialog-container {
    transform: translateX(-50%) !important;
  }
}

Now my dialog is always 24px below the anchor element and centered horizontally relative to it. Hope this helps someone.

Max Kurtz
  • 448
  • 5
  • 17