是否可以在特定的DOM元素上打开angular / Material2 Snackbar?

时间:2018-07-24 19:48:41

标签: angular angular-material2 angular-material-6

组件:https://material.angular.io/components/snack-bar/examples

我有一个页面是父部分(“ #wrapper_container”)。我想在“ #wrapper_element”子元素的中心弹出小吃店。

谢谢

3 个答案:

答案 0 :(得分:1)

是的。 viewContainerRef的{​​{1}}属性将接受MatSnackBarConfig用作将其ViewContainer附加到其上的位置。

获取元素Portal可以通过ViewChild查询或将其注入到放置在元素上的指令中来完成。前者是一个更简单的选择:

component.html:

ViewContainerRef

component.ts:

<div #wrapperContainer> </div>

答案 1 :(得分:1)

您应该检查他们的API,这里有参数viewContainerRef,也可以用verticalPosition定义高度。

答案 2 :(得分:1)

我们无法使用viewContainerRef选项将快餐栏附加到自定义容器。在Angular Material docs中,此选项为:

出于依赖关系注入的目的,作为快餐栏的父级的视图容器。

注意:这不影响在DOM中插入快餐栏的位置。

ComponentPortal documentation,我们可以找到对viewContainerRef的更详细的描述:

附加组件应位于Angular的逻辑组件树中的位置。 这与组件呈现的位置不同,后者是由PortalOutlet确定的。当主机位于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 -<snack-bar-container></snack-bar-container>
  5. Component -我们的Snackbar,其打开方式为:this.snackbar.open(message, action)

现在,我们真正需要更改的是OverlayContainer在DOM中的位置,根据docs,这是:

所有单个叠加层元素都位于其中的容器元素 呈现。

默认情况下,覆盖容器直接附加到文档主体。

从技术上讲,可以使用自定义提供程序来提供自定义覆盖容器,如documentation中所述:

@NgModule({
  providers: [{provide: OverlayContainer, useClass: AppOverlayContainer}],
 // ...
})
export class MyModule { }

但是,不幸的是,这是一个单例,并造成了潜在的问题:

MatDialogMatSnackbarMatTooltipMatBottomSheet和所有使用OverlayContainer的组件都将在此AppOverlayContainer中打开

由于这种情况远非理想,因此我编写的自定义提供程序(AppOverlayContainer仅需要按需更改overlaycontainer的位置。如果未调用,则将overlaycontainer附加到body上。

代码是:

import { OverlayContainer } from '@angular/cdk/overlay';

export class AppOverlayContainer extends OverlayContainer {
  customContainerClass = 'app-overlay-container';

  /**
   * Append the OverlayContainer to the custom element
   */
  public appendToCustomContainer(element: HTMLElement): void {
    if (element === null) {
      return;
    }

    if (!this._containerElement) {
      super._createContainer();
    }

    // we need to add a custom css class
    this._containerElement.classList.add(this.customContainerClass);

    // this._containerElement is 'cdk-overlay-container'
    // the code below attaches it to our custom container
    element.appendChild(this._containerElement);
  }

  /**
   * Remove the OverlayContainer from the custom element and append it to body
   */
  public removeFromCustomContainer(): void {
    if (!this._containerElement) {
      return;
    }


   this._containerElement.classList.remove(this.customContainerClass);

    // this._containerElement is 'cdk-overlay-container'
    // the code below re-attaches it to body
    this._document.body.appendChild(this._containerElement);
  }

}

因此,在打开小吃店之前,我们必须致电:

AppOverlayContainer.appendToCustomContainer(HTMLElement).

方法,该方法将overlaycontainer附加到我们的自定义包装元素。

小吃店关闭时,最好致电:

AppOverlayContainer.removeFromCustomContainer();

方法,该方法将从我们的自定义包装元素中删除overlaycontainer,并将其重新附加到body

示例:

如果我们要在自定义容器中显示小吃店,#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中,我们有:

export class SnackBarOverviewExample {

  constructor(
    private _snackBar: MatSnackBar,
    private _appOverlayContainer: OverlayContainer
  ) { }

  openSnackBar(
    appOverlayWrapperContainer: HTMLElement, 
    message: string, 
    action: string
  ) {

    if (appOverlayWrapperContainer === null) {
      return;
    }

    (this._appOverlayContainer as AppOverlayContainer).appendToCustomContainer(appOverlayWrapperContainer);

    this.snackbarRef = this._snackBar.open(message, action, {
      duration: 2000
    });
 }
}

此外,我们需要在应用程序通用样式表中插入一些CSS:

.app-overlay-container {
  position: absolute;
}

完整代码和演示都在Stackblitz上,如果我已经实现了多个快餐栏容器:

https://stackblitz.com/edit/angular-wksbmf-s15ms4?file=src%2Fapp%2Fapp.component.ts