有什么方法可以修改MatDialog叠加顺序吗?

问题描述 投票:0回答:3

我想在我的应用程序中实现基本的对话框处理,有两个功能:

  1. 要关闭所有打开的对话框:使用 MatDialog.closeAll();
  2. 即可轻松完成
  3. 使用户能够在打开的对话框之间切换!

这对我来说是个问题,因为似乎没有功能可以使对话框成为顺序中的第一个对话框。

我可以使用 MatDialog.openDialogs 查询所有打开的对话框,并且可以显示它们的名称以供用户选择。之后,我尝试向单击的面板类添加一个面板类来修改 z-index,但什么也没发生。

解决办法是什么?

我的代码:

菜单的 HTML 模板,用于处理对话框操作:

<mat-menu #menu="matMenu">
  <button mat-menu-item (click)="closeAll()">
    <mat-icon>close_all</mat-icon>
    <span>Close all</span>
  </button>
  <mat-divider></mat-divider>
  <button
    (click)="focusDialog(dialog, $event)"
    *ngFor="let dialog of this.dialog.openDialogs"
    mat-menu-item
  >
    <mat-icon>preview</mat-icon>
    <span>{{ dialog.componentInstance.data.title }}</span>
  </button>
</mat-menu>

按钮点击事件处理程序:

focusDialog(dialogRef: MatDialogRef<any>, e: any) {
    
    e.stopPropagation();
    this.dialog.openDialogs.forEach((dialog) => {
      dialog.removePanelClass('dialog-focus');
    });
    dialogRef.addPanelClass('dialog-focus');
  }

对话框焦点 CSS 类:

.dialog-focus .mat-dialog-container{
  z-index: 1000 !important;
}

编辑:

我似乎无法使用 z-index 实现任何目标,但我可以将对话框的显示设置为“阻止”或“无”,这样,我就可以看到首选对话框。 问题是:我无法与它交互!最后打开的对话框的覆盖仍然存在。我怎样才能让它消失,然后在用户切换回相关对话框时再次使用它?

focusDialog(dialogRef: MatDialogRef<any>, e: any) {
    e.stopPropagation();
    this.dialog.openDialogs.forEach((dialog) => {
      dialog.removePanelClass('dialog-focus');
      dialog.addPanelClass('dialog-background');
    });
    dialogRef.addPanelClass('dialog-focus');
    dialogRef.removePanelClass('dialog-background');
  }
.dialog-focus .mat-dialog-container {
  z-index: 1000 !important;
  display: block;
}

.dialog-background .mat-dialog-container {
  z-index: 1 !important;
  display: none;
}

我检查了 HTML 树,似乎打开的对话框和覆盖层是同级的。

angular typescript angular-material dialog modal-dialog
3个回答
1
投票

mat-dialog 将类为“cdk-global-overlay-wrapper”的 div 添加到 .html 底部类为“cdk-overlay-container”的 div

所以你可以改变元素的位置。但你需要用“纯javascript”来制作。

我们可以更改在模式中每个组件中创建函数的元素的位置,但最好是由父级来完成工作(您也可以在服务中委托(*))

因此,我们在模态组件的构造函数中注入 elementRef 并声明一个输出

  @Output() toTop:EventEmitter<any>=new EventEmitter<any>()
  constructor(public el: ElementRef){}

当我们想要鼠标按下时,我们会发出事件。在 this stackbliz 中,将 cdkdrag 添加到我选择的组件的“标题”中,同时将鼠标向下“拖动”

<!--as I declared in constructor as public the "el" we can use in .html-->
<h1 (mousedown)="toTop.emit(el)" mat-dialog-title cdkDragHandle 
    >Hi {{data.name}}</h1>

在父级中我们需要一个函数来使我们的元素位于顶部

  toTop(wich:ElementRef) {
    const elements = document.getElementsByClassName(
      'cdk-global-overlay-wrapper'
    );

    for (let i = 0; i < elements.length; ++i) {
      const el = elements[i];
      if (el.contains(wich.nativeElement)) {
        const parent = el.parentNode;
        const last = parent.lastChild;
        if (last != el){
          last.parentNode.insertBefore(el, last.nextSibling);
        }
        break;
      }
    }
  }

并且,在此基础上另一个SO与我们制作的父级进行通信,例如

const dialogRef = this.dialog.open(DialogOverviewExampleDialog, {
  width: '250px',
  hasBackdrop: false,
  data: {name: this.name, animal: this.animal}
});
const sub = dialogRef.componentInstance.toTop.subscribe((el) => {
  this.toTop(el)
});

(*) 使用服务只需使用函数声明您的服务

toTop(wich:ElementRef) {
  ....
}

将服务注入模态组件中

   constructor(public modalService:ModalService,
               public el:ElementRef){}

并使用

<h1 (mousedown)="modalService.toTop(el)" mat-dialog-title cdkDragHandle 
        >Hi {{data.name}}</h1>

0
投票

根据 Eliseo's 的回答,我想出了另一个解决方案,它更适合我的应用程序:

我不希望父组件管理覆盖顺序更改,因为我认为它更通用,如果我创建一个负责所有对话框操作的组件,并接受一个组件作为输入以将其模板加载到其自身中。这样我就可以在应用程序中的任何位置轻松打开 dilaog,并在对话框中打开对话框等等。

根据 Eliseo 的回答,不需要修改面板类。重新排序 cdk-global-overlay-wrapper 元素就足够了。

解决方案的 Stackblitz 链接:

https://stackblitz.com/edit/angular-9-material-starter-8uhrgj?file=src/app/viewer-modal/viewer-modal.component.ts

原问题中的文件如何变化:

HTML

<mat-menu #menu="matMenu">
  <button mat-menu-item (click)="closeAll()">
    <mat-icon>close_all</mat-icon>
    <span>Close all</span>
  </button>
  <mat-divider></mat-divider>
  <button
    (click)="focusDialog(dialog, $event)"
    *ngFor="let dialog of this.dialog.openDialogs"
    mat-menu-item
  >
    <mat-icon>preview</mat-icon>
    <span>{{ dialog.componentInstance.data.title }}</span>
  </button>
</mat-menu>
focusDialog(dialogRef: MatDialogRef<any>, e: any) {
    const cdkOverlayWrappers = document.getElementsByClassName(
      'cdk-global-overlay-wrapper'
    );

    for (let i = 0; i < cdkOverlayWrappers.length; i++) {
      const wrapper = cdkOverlayWrappers[i];

      if (wrapper.contains(dialogRef.componentInstance.el.nativeElement)) {
        const parent = wrapper.parentNode!;
        const last = parent.lastChild;
        if (last != wrapper)
          last!.parentNode!.insertBefore(wrapper, last!.nextSibling);
        break;
      }
    }
  }

0
投票

我通过改变 z-index 来做到这一点

export function setDialogInFront(elementHost: {
  elementRef: { nativeElement: HTMLElement };
}): void {
  const element = elementHost.elementRef.nativeElement;
  const mainOverlayWrapper = element.closest(
    '.cdk-global-overlay-wrapper'
  ) as HTMLElement;

  // the backdrop is the element before the main overlay wrapper
  // could be null if the dialog config contains hasBackdrop: false
  const mainOverlayBackDrop =
    mainOverlayWrapper.previousElementSibling as HTMLElement | null;

  // get the max z-index of the other dialogs
  let maxZIndex = 0;
  document
    .querySelectorAll('.cdk-global-overlay-wrapper')
    .forEach((wrapper) => {
      const zIndex = parseInt(
        window.getComputedStyle(wrapper).getPropertyValue('z-index'),
        10
      );
      maxZIndex = Math.max(maxZIndex, zIndex);
    });

  // set the z-index of the main dialog to be one higher than the max
  mainOverlayWrapper.style.zIndex = `${maxZIndex + 1}`;

  if (mainOverlayBackDrop) {
    mainOverlayBackDrop.style.zIndex = `${maxZIndex + 1}`;
  }
}

在您的对话框组件中

export class DialogComponent {
  public elementRef: ElementRef<HTMLElement> = inject(ElementRef);
}

将组件实例传递给函数

setDialogInFront(dialogRef.componentInstance);
© www.soinside.com 2019 - 2024. All rights reserved.