我想在我的应用程序中实现基本的对话框处理,有两个功能:
这对我来说是个问题,因为似乎没有功能可以使对话框成为顺序中的第一个对话框。
我可以使用 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 树,似乎打开的对话框和覆盖层是同级的。
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>
根据 Eliseo's 的回答,我想出了另一个解决方案,它更适合我的应用程序:
我不希望父组件管理覆盖顺序更改,因为我认为它更通用,如果我创建一个负责所有对话框操作的组件,并接受一个组件作为输入以将其模板加载到其自身中。这样我就可以在应用程序中的任何位置轻松打开 dilaog,并在对话框中打开对话框等等。
根据 Eliseo 的回答,不需要修改面板类。重新排序 cdk-global-overlay-wrapper 元素就足够了。
解决方案的 Stackblitz 链接:
原问题中的文件如何变化:
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;
}
}
}
我通过改变 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);