问题
I have made an Angular 6 app where I have some MatDialog. This works perfectly in development mode.
Now I have to implement this into an existing website (that uses sitecore 9). This works great, except that the dialogs won't open correctly. I've found out that the dialogs are added to the body and not to my angular root DOM element.
<html>
<head></head>
<body>
<app-library>...</app-library>
<div class="cdk-overlay-container">...</div>
</body>
</html>
Is there a way to add the dialog to the angular app element (in my case ) and not to the body element?
I've already tried to use the viewContainerRef option matDialog, but this doesn't seem to work:
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
});
}
}
回答1:
According to Angular Material docs, viewContainerRef
option of MatDialogConfig
is:
Where the attached component should live in Angular's logical component tree. This affects what is available for injection and the change detection order for the component instantiated inside of the dialog.
This does not affect where the dialog content will be rendered.
When inspecting the DOM when a dialog is opened, we'll find the following component hierarchy:
Body
-<body>
OverlayContainer
-<div class="cdk-overlay-container"></div>
Overlay
-<div id="cdk-overlay-{id}" class="cdk-overlay-pane">
ComponentPortal
-<mat-dialog-container></mat-dialog-container>
Component
- ourComponentExampleDialog
component, which is opened like:this.dialog.open(ComponentExampleDialog)
.
Now, what we really need to change is the position in the DOM of the OverlayContainer
, which, according to docs, is:
the container element in which all individual overlay elements are rendered.
By default, the overlay container is appended directly to the document body.
Technically, it is possible to provide a custom overlay container, as described in documentation, but, unfortunately, it is a singleton and creates a potential problem:
MatDialog
, MatSnackbar
, MatTooltip
, MatBottomSheet
and all components that use the OverlayContainer
will open within this custom container.
If this is not a problem, use the following code, which attaches the overlay container to the element with 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;
}
}
}
Then, add the following 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;
}
Stackblitz demo: https://stackblitz.com/edit/angular-wksbmf
Also the following article might be useful, because it contains a similar implementation: https://blog.bbogdanov.net/post/angular-material-overlay-hack-overlay-container/
来源:https://stackoverflow.com/questions/52763285/add-matdialog-popup-to-angular-root-and-not-to-body