有没有办法修改MatDialog的覆盖顺序?

Is there any way to modify the MatDialog overlay order?

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

  1. 关闭所有打开的对话框:使用 MatDialog.closeAll();
  2. 很简单
  3. 允许用户在打开的对话框之间切换!

这对我来说是个问题,因为似乎没有让对话框按顺序排在第一个的功能。

我可以用MatDialog.openDialogs查询所有打开的对话框,我可以显示它们的名称供用户选择。之后,我尝试将面板 class 添加到修改 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 class:

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

编辑:

我似乎无法通过 z-index 实现任何效果,但我可以将对话框的显示设置为 'block' 或 'none' 这样,我就可以看到首选对话框。 问题是:我无法与之交互! 最后打开的对话框的叠加层仍然存在。我怎样才能让它消失,然后在用户切换回其相关对话框时再次使用它?

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 树,打开的对话框和叠加层似乎是兄弟。

A mat-dialog 添加一个 div with class 'cdk-global-overlay-wrapper' to a div with class 'cdk-overlay-container' 在底部.html

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

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

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

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

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

<!--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>

好吧,在 parent 中,我们需要一个函数让我们的元素置顶

  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 将 mat-dialog 与我们制作的 parent 进行通信,例如

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>

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

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

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

Stackblitz link 解决方案:

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;
      }
    }
  }