将MatDialog弹出窗口添加到Angular Root而不是body



我做了一个Angular 6应用程序,我有一些MatDialog。这在开发模式下完美运行。

现在,我必须将其实施到现有网站(使用 sitecore 9(中。 这很好用,除了对话框无法正确打开。 我发现对话框被添加到正文中,而不是添加到我的角度根 DOM 元素中。

<html>
<head></head>
<body>
<app-library>...</app-library>
<div class="cdk-overlay-container">...</div>
</body>
</html>

有没有办法将对话框添加到角度应用程序元素(在我的情况下(而不是正文元素?

我已经尝试使用viewContainerRef选项matDialog,但这似乎不起作用:

export class AppComponent implements OnInit {
constructor(..., public viewRef: ViewContainerRef) {
}
export class FilterToggleComponent implements OnInit {
constructor(..., public dialog: MatDialog, private appRef: ApplicationRef) {}
openFilterPanel() {
const viewRef = (this.appRef.components[0].instance as AppComponent).viewRef;
const dialogRef = this.dialog.open(FilterPanelComponent, {
width: '100vw',
height: '100%',
panelClass: 'search-filter-panel-modal',
viewContainerRef: viewRef
});
}
}

根据 Angular Material 文档,viewContainerRefMatDialogConfig选项是:

附加组件应位于 Angular 的逻辑组件树中的位置。这会影响可用于注入的内容以及在对话框中实例化的组件的更改检测顺序。

这不会影响对话框内容的呈现位置。

在打开对话框时检查 DOM 时,我们会找到以下组件层次结构:

  1. Body-<body>
  2. OverlayContainer-<div class="cdk-overlay-container"></div>
  3. Overlay-<div id="cdk-overlay-{id}" class="cdk-overlay-pane">
  4. ComponentPortal-<mat-dialog-container></mat-dialog-container>
  5. Component- 我们的ComponentExampleDialog组件,打开方式如下:this.dialog.open(ComponentExampleDialog).

现在,我们真正需要改变的是OverlayContainerDOM 中的位置 ,根据文档,它是:

容器元素,其中所有单独的覆盖元素都位于其中 呈现。

默认情况下,覆盖容器直接追加到文档正文。

从技术上讲,可以提供自定义覆盖容器,如文档中所述,但不幸的是,它是一个单例并会产生潜在问题:

MatDialogMatSnackbarMatTooltipMatBottomSheet以及使用该OverlayContainer的所有组件都将在此自定义容器中打开。

如果这不是问题,请使用以下代码,该代码使用id="angular-app-root"将覆盖容器附加到元素:

@NgModule({
providers: [{provide: OverlayContainer, useClass: AppOverlayContainer}],
// ...
})
export class MyModule { }
export class AppOverlayContainer extends OverlayContainer {
protected _createContainer(): void {
const container: HTMLDivElement = document.createElement('div');
container.classList.add('app-overlay-container');
const element: Element | null = document.querySelector('#angular-app-root');
if (element !== null) {
element.appendChild(container);
this._containerElement = container;
}
}
}

然后,添加以下 css:

.app-overlay-container {
position: absolute;
z-index: 1000;
pointer-events: none;
top: 0;
left: 0;
height: 100%;
width: 100%;
}
.app-overlay-container:empty {
display: none;
}

堆栈闪电战演示:https://stackblitz.com/edit/angular-wksbmf

以下文章也可能很有用,因为它包含类似的实现: https://blog.bbogdanov.net/post/angular-material-overlay-hack-overlay-container/

@andreivictor的解决方案对我有用(使用position: fixed(,并确保我的背景和微调器仅覆盖Angular Elements Web组件。

我的应用程序中也有一个mat-drawer。 最初,我将app-overlay-container附加到包含在mat-drawer-content中的组件。mat-drawer打开时,包含一个包含选择(下拉(元素的表单。 虽然其他一切正常,但选择不再可见,因此无法使用。 我发现mat-select-panel元素出现在app-overlay-container中,这被产生mat-select-panelmat-drawer所掩盖。

吸取的教训是,app-overlay-container确实必须添加到 Angular Elements Web 组件中最外层的元素中。 我最终在app.component.html中添加了一个额外的包装器/容器div,纯粹是为了实现这一目标,我不愿意这样做,但这是我能让它工作的唯一方法。 包装器div需要显式指定widthheight,而以前最外层的元素需要width: 100%height: 100%。 (但是YMMV作为其中一些可能特定于我的个人项目。

最新更新