组件:https ://material.angular.io/components/snack-bar/examples
我有一个页面是父部分('#wrapper_container')。我想在“#wrapper_element”的子元素的中心弹出小吃店。
谢谢
组件:https ://material.angular.io/components/snack-bar/examples
我有一个页面是父部分('#wrapper_container')。我想在“#wrapper_element”的子元素的中心弹出小吃店。
谢谢
我们不能使用viewContainerRef
选项来将小吃店附加到自定义容器。从 Angular Material docs,这个选项是:
用作依赖注入目的的快餐栏的父视图容器。###注意:这不会影响小吃栏在 DOM 中的插入位置。###
从ComponentPortal
文档中,我们可以找到更详细的描述viewContainerRef
:
附加组件应该位于 Angular 的逻辑组件树中的位置。这与组件呈现的位置不同,后者由 PortalOutlet 确定。当宿主在 Angular 应用程序上下文之外时,源是必需的。
当快餐店打开时检查 DOM 时,我们会发现以下组件层次结构:
Body
-<body>
OverlayContainer
-<div class="cdk-overlay-container"></div>
Overlay
-<div id="cdk-overlay-{id}" class="cdk-overlay-pane">
ComponentPortal
-<snack-bar-container></snack-bar-container>
Component
- our Snackbar
,打开方式如下:this.snackbar.open(message, action)
.现在,我们真正需要改变的是 DOM 在 DOM 中的位置OverlayContainer
,根据docs,它是:
渲染所有单个覆盖元素的容器元素。
默认情况下,覆盖容器直接附加到文档正文中。
从技术上讲,可以通过使用自定义提供程序来提供自定义覆盖容器,如文档中所述:
@NgModule({
providers: [{provide: OverlayContainer, useClass: AppOverlayContainer}],
// ...
})
export class MyModule { }
一个简单的实现将是发布在这里的那个。
但是,不幸的是,它是一个单例并且会产生一个潜在的问题:
MatDialog
, MatSnackbar
,MatTooltip
和MatBottomSheet
所有使用 的组件都OverlayContainer
将在此 AppOverlayContainer 中打开:
由于这种情况远非理想,我编写的自定义提供程序 ( AppOverlayContainer
) 需要更改overlaycontainer
only on demand 的位置。如果没有调用,它会让overlaycontainer
被附加到body
.
代码是:
import { Injectable } from '@angular/core';
import { OverlayContainer } from '@angular/cdk/overlay';
@Injectable({
providedIn: 'root'
})
export class AppOverlayContainer extends OverlayContainer {
appOverlayContainerClass = 'app-cdk-overlay-container';
/**
* Append the OverlayContainer to the custom wrapper element
*/
public appendToCustomWrapper(wrapperElement: HTMLElement): void {
if (wrapperElement === null) {
return;
}
// this._containerElement is 'cdk-overlay-container'
if (!this._containerElement) {
super._createContainer();
}
// add a custom css class to the 'cdk-overlay-container' for styling purposes
this._containerElement.classList.add(this.appOverlayContainerClass);
// attach the 'cdk-overlay-container' to our custom wrapper
wrapperElement.appendChild(this._containerElement);
}
/**
* Remove the OverlayContainer from the custom element and append it to body
*/
public appendToBody(): void {
if (!this._containerElement) {
return;
}
// remove the custom css class from the 'cdk-overlay-container'
this._containerElement.classList.remove(this.appOverlayContainerClass);
// re-attach the 'cdk-overlay-container' to body
this._document.body.appendChild(this._containerElement);
}
}
因此,在打开小吃店之前,我们必须调用:
AppOverlayContainer.appendToCustomWrapper(HTMLElement).
方法,它将 附加overlaycontainer
到我们的自定义包装器元素。
当小吃店关门时,最好拨打以下电话:
AppOverlayContainer.appendToBody();
方法,它overlaycontainer
从我们的自定义包装元素中删除 并将其重新附加到body
.
此外,由于AppOverlayContainer
将被注入到我们的组件中并且需要保持单例,我们将通过使用useExisting
语法来提供它:
providers: [
{provide: OverlayContainer, useExisting: AppOverlayContainer}
]
例子:
如果我们想在自定义容器中显示小吃吧, #appOverlayWrapperContainer1
:
<button mat-raised-button
(click)="openSnackBar(appOverlayWrapperContainer1, 'Snackbar 1 msg', 'Action 1')">
Open Snackbar 1
</button>
<div class="app-overlay-wrapper-container" #appOverlayWrapperContainer1>
Snackbar 1 overlay attached to this div
</div>
在我们的组件.ts
中,我们有:
import { AppOverlayContainer } from './app-overlay-container';
export class SnackBarOverviewExample {
constructor(
private _snackBar: MatSnackBar,
private _appOverlayContainer: AppOverlayContainer
) { }
openSnackBar(
overlayContainerWrapper: HTMLElement,
message: string,
action: string
) {
if (overlayContainerWrapper === null) {
return;
}
this.appOverlayContainer.appendToCustomWrapper(overlayContainerWrapper);
this.snackbarRef = this._snackBar.open(message, action, {
duration: 2000
});
}
}
此外,我们需要一些必须插入到应用通用样式表中的 css:
.app-cdk-overlay-container {
position: absolute;
}
完整的代码和演示在 Stackblitz 上,我是否已经实现了多个小吃店容器:
是的。的viewContainerRef
属性MatSnackBarConfig
将接受一个ViewContainer
用作附加它的位置Portal
。
ViewContainerRef
可以通过 ViewChild 查询或将其注入到放置在元素上的指令中来获取元素的 a 。前者是一个更简单的选择:
组件.html:
<div #wrapperContainer> </div>
组件.ts:
@ViewChild('wrapperContainer', { read: ViewContainerRef }) container: ViewContainerRef;
ngAfterViewInit() {
this.something = this.snackBarService.open(
'message text',
'button text',
{ viewContainerRef: this.container}
);
}
你应该检查他们的API,有参数viewContainerRef
或者你可以用verticalPosition
.