如何根据页面偏移将 Angular CDK Overlay 组件保留在页面内
How to keep Angular CDK Overlay component within page based on page offset
我正在处理我的图书馆 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,但这并没有考虑到屏幕外的元素。
打开覆盖的class:
@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();
}
}
}
我查看了GitHub中的源代码。由于您使用指令将上下文菜单附加到元素,因此您还可以在指令中获取元素引用。只需将它添加到指令的构造函数中,然后将其发送到您的服务,您就可以使用它。这是您的 class 修改以获取 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);
}
}
事实证明,您毕竟可以使用 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
};
}
我正在处理我的图书馆 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,但这并没有考虑到屏幕外的元素。
打开覆盖的class:
@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();
}
}
}
我查看了GitHub中的源代码。由于您使用指令将上下文菜单附加到元素,因此您还可以在指令中获取元素引用。只需将它添加到指令的构造函数中,然后将其发送到您的服务,您就可以使用它。这是您的 class 修改以获取 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);
}
}
事实证明,您毕竟可以使用 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
};
}