如何基于页面偏移量将Angular CDK Overlay组件保留在页面内

时间:2018-11-18 19:10:06

标签: angular overlay angular-cdk

我正在图书馆Contexr上工作。我只是重构了我的应用程序以使用Angular CDK Overlay来显示上下文菜单,所以我不再需要在实际应用程序中包括一些组件(减少了一个安装步骤)。

我曾经使用FlexibleConnectedPositionStrategy在元素下面创建一个下拉菜单,该下拉菜单将保留在页面内。这种排名策略的创建类似于使用ElementRef:

const positionStrategy = this.overlay.position()
      .flexibleConnectedTo(elementRef)
      .left(state.left + 'px')
      .top(state.top + 'px');

问题是我没有ElementRef。我的叠加层应灵活连接到.left()和.top()。是否可以通过FlexibleConnectedPositionStrategy做到这一点?目前,我正在尝试使用GlobalPositionStrategy,但这不能解释屏幕外的元素。

打开覆盖图的类:

@Injectable({
  providedIn: 'root'
})
export class ContextMenuService {
  private overlayRef: OverlayRef;

  constructor(private overlay: Overlay, private injector: Injector) {}

  public open(state: ContextState) {
    const overlayConfig = this.getOverlayConfig(state);
    this.overlayRef = this.overlay.create(overlayConfig);
    const contextMenuRef = new ContextMenuOverlayRef(this.overlayRef);
    this.attachDialogContainer(this.overlayRef, state, contextMenuRef);
  }

  private getOverlayConfig(state: ContextState) {
    const positionStrategy = this.overlay.position()
      .global()
      .left(state.left + 'px')
      .top(state.top + 'px');

    return {
      positionStrategy: positionStrategy
    };
  }

  private createInjector(state: ContextState, dialogRef: ContextMenuOverlayRef) {
    const injectionTokens = new WeakMap();
    injectionTokens.set(ContextMenuOverlayRef, dialogRef);
    injectionTokens.set(CONTEXT_MENU_OVERLAY_DATA, state);
    return new PortalInjector(this.injector, injectionTokens);
  }

  private attachDialogContainer(overlayRef: OverlayRef, state: ContextState, contextMenuOverlayRef: ContextMenuOverlayRef) {
    const injector = this.createInjector(state, contextMenuOverlayRef);
    const containerPortal = new ComponentPortal(ContextMenuComponent, null, injector);
    overlayRef.attach(containerPortal);
  }

  public close() {
    if (this.overlayRef) {
      this.overlayRef.dispose();
    }
  }
}

2 个答案:

答案 0 :(得分:3)

事实证明,您毕竟可以使用FlexibleConnectedPositionStrategy。我在Github上找到了一个名为ngrx-rightclick的上下文菜单库(谢谢!)。他们在这里基于click事件创建了一个新的ElementRef。

private getOverlayConfig(event: MouseEvent, state: ContextState) {
    const target = {
      getBoundingClientRect: (): ClientRect => ({
        bottom: event.clientY,
        height: 0,
        left: event.clientX,
        right: event.clientX,
        top: event.clientY,
        width: 0,
      }),
    };

    const element = new ElementRef(target);

    const positionStrategy = this.overlay.position()
      .flexibleConnectedTo(element)
      .withFlexibleDimensions(false)
      .withPositions([
        {
          originX: 'end',
          originY: 'top',
          overlayX: 'start',
          overlayY: 'top',
        },
        {
          originX: 'start',
          originY: 'top',
          overlayX: 'end',
          overlayY: 'top',
        },
        {
          originX: 'end',
          originY: 'bottom',
          overlayX: 'start',
          overlayY: 'bottom',
        },
        {
          originX: 'start',
          originY: 'bottom',
          overlayX: 'end',
          overlayY: 'bottom',
        },
      ]);

    return {
      positionStrategy: positionStrategy
    };
  }

答案 1 :(得分:1)

我查看了GitHub中的源代码。由于您使用指令将上下文菜单附加到元素,因此您还可以在指令中获取元素引用。只需将其添加到指令的构造函数中,然后将其发送到您的服务即可使用。这是修改您的类以获取ElementRef的信息:

import {Directive, HostListener, Input} from '@angular/core';
import {ContexrService} from '../providers/contexr.service';

@Directive({
  selector: '[ctx]'
})
export class ContextDirective {
  @Input('ctx') ctx: string;
  @Input('ctxArgs') ctxArgs: any;

  constructor(private contexr: ContexrService, private elementRef: ElementRef<any>) {}

  @HostListener('contextmenu', ['$event'])
  @HostListener('click', ['$event'])
  public onContextMenu(event) {
    this.contexr.addCurrentContext(this.ctx, this.ctxArgs, this.elementRef);
  }
}