Как сохранить компонент Angular CDK Overlay на странице на основе смещения страницы

Я работаю над своей библиотекой 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();
    }
  }
}

person Scuba Kay    schedule 18.11.2018    source источник
comment
prideparrot.com/blog/archive/2019/3/   -  person VJAI    schedule 02.03.2019


Ответы (2)


Оказывается, вы все-таки можете использовать FlexibleConnectedPositionStrategy. Я нашел некоторую библиотеку контекстного меню на Github, которая называется ngrx-rightclick (спасибо!). Здесь они создали новый 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
    };
  }
person Scuba Kay    schedule 19.11.2018

Я посмотрел исходный код в 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);
  }
}
person AlesD    schedule 18.11.2018
comment
Директива используется в отношениях родитель-потомок. Меню addCurrentContext может запускаться несколько раз одним щелчком мыши. Возможно, я мог бы отследить первую директиву, по которой щелкнули мышью, но FlexibleConnectedPositionStrategy появится на одной из сторон моего элемента. Мне нужно, чтобы он был в месте щелчка ... - person Scuba Kay; 19.11.2018
comment
Из того, что документация говорит, что ConnectedPositionStrategy и FlexibleConnectedPositionStrategy относятся к элемент. Так что единственный способ заставить это работать - это как-то запомнить элемент или его положение. В противном случае вы можете попробовать реализовать собственный PositionStrategy. - person AlesD; 19.11.2018
comment
Я нашел пример здесь. Я попробую создать собственный PositionStrategy. Спасибо за совет! - person Scuba Kay; 19.11.2018