3

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

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

谢谢

4

3 回答 3

3

我们不能使用viewContainerRef选项来将小吃店附加到自定义容器。从 Angular Material docs,这个选项是:

用作依赖注入目的的快餐栏的父视图容器。###注意:这不会影响小吃栏在 DOM 中的插入位置。###

ComponentPortal 文档中,我们可以找到更详细的描述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- our Snackbar,打开方式如下:this.snackbar.open(message, action).

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

渲染所有单个覆盖元素的容器元素。

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

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

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

一个简单的实现将是发布在这里的那个。


但是,不幸的是,它是一个单例并且会产生一个潜在的问题:

MatDialog, MatSnackbar,MatTooltipMatBottomSheet所有使用 的组件都OverlayContainer将在此 AppOverlayContainer 中打开

由于这种情况远非理想,我编写的自定义提供程序 ( AppOverlayContainer) 需要更改overlaycontaineronly 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 上,我是否已经实现了多个小吃店容器:

https://stackblitz.com/edit/angular-ivy-wxthzy

于 2020-04-11T17:30:57.573 回答
2

是的。的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}
  );

}
于 2018-07-24T20:02:42.093 回答
1

你应该检查他们的API,有参数viewContainerRef或者你可以用verticalPosition.

于 2018-07-24T20:06:37.860 回答