组件: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
选项来将 Snackbar 附加到自定义容器。根据 Angular Material 的 文档,此选项是:
从作为 snackbar 的父级以进行依赖注入的视图容器。 ###注意:这不会影响 snackbar 插入 DOM 的位置。###
ComponentPortal
文档 中,我们可以找到 viewContainerRef
的更详细描述:
在打开 Snackbar 时检查 DOM,我们会发现以下组件层次结构:所附加组件在 Angular 的逻辑组件树中应该存在的位置。这与组件呈现的位置不同,后者由 PortalOutlet 确定。当宿主位于 Angular 应用程序上下文之外时,必须指定源。
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
- 我们的 Snackbar,例如:this.snackbar.open(message, action)
。OverlayContainer
在 DOM 中的位置,根据 文档,它是:
从技术上讲,可以像 文档 中描述的那样,通过使用自定义提供程序来提供 自定义 覆盖容器。所有单独的覆盖元素都渲染的容器元素。
默认情况下,覆盖容器直接附加到文档主体。
@NgModule({
providers: [{provide: OverlayContainer, useClass: AppOverlayContainer}],
// ...
})
export class MyModule { }
一个简单的实现可以在这里找到。
但是,不幸的是,它是一个单例对象,并且可能会引起问题:
MatDialog
、MatSnackbar
、MatTooltip
、MatBottomSheet
和所有使用 OverlayContainer
的组件将在此 AppOverlayContainer 中打开:
由于这种情况远非理想,我编写的自定义提供程序(AppOverlayContainer
)需要根据需要仅更改overlaycontainer
的位置。如果没有调用,则允许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
附加到我们的自定义包装元素。
当 snackbar 关闭时,最好调用:
AppOverlayContainer.appendToBody();
该方法将从我们的自定义包装元素中移除overlaycontainer
并重新附加到body
。
此外,由于AppOverlayContainer
将被注入到我们的组件中并需要保持单例模式,因此我们将使用useExisting
语法来提供它:
providers: [
{provide: OverlayContainer, useExisting: AppOverlayContainer}
]
示例:
如果我们想要在自定义容器#appOverlayWrapperContainer1
中显示 Snackbar:
<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
});
}
}
.app-cdk-overlay-container {
position: absolute;
}
完整代码和演示已经上传至 Stackblitz,这里我实现了多个 Snackbar 容器:
MatSnackBarConfig
的 viewContainerRef
属性将接受一个 ViewContainer
,用作将其 Portal
附加到的位置。ViewContainerRef
。前者是更容易的选项:<div #wrapperContainer> </div>
component.ts:
@ViewChild('wrapperContainer', { read: ViewContainerRef }) container: ViewContainerRef;
ngAfterViewInit() {
this.something = this.snackBarService.open(
'message text',
'button text',
{ viewContainerRef: this.container}
);
}
verticalPosition
只接受两个值:top
和 bottom
。 - Edric
MatDialog
中可以工作,所以我想吐司条也应该一样。我猜两者都与它们的Portal
有关。你可以像平常一样打开 snackbar,给它一个组件用作 snackbar,从该组件获取templateRef
,然后从 CDK 创建一个新的Portal
并在那里呈现templateRef
,但这是完全错误的。不幸的是,在这个 bug 得到解决之前,你需要使用 hacky 的方法来修复它。 - joh04667