中心Mat-Menu,中心MatMenu与按钮叠加 [英] Center Mat-Menu, Center MatMenu Overlay to Button
问题描述
每个
我选择了一个指令,因为 MatMenuTrigger
是指令
,复制材料源使用的逻辑很有意义。
到目前为止,这是我想出的方法,我不确定进入这样的类本身是否可以接受,或者不确定是否有这样做的负面影响。
对于这种建议或改进,我愿意就此方法进行建设性讨论。
Stackblitz
本质上来说,我将 matMenuTrigger
与通过将按钮放置在 div
包裹 mat-menu
的按钮上...这是我可以通过编程方式打开
- 我还将
menuTrigger
分配给div
上的templateRef
并将其作为输入传递给我的center-mat-menu
选择器
<按钮mat-button [center-mat-menu] = menuTrigger> Menu< / button>
< div#menuTrigger = matMenuTrigger [matMenuTriggerFor] =菜单>
< mat-menu#menu = matMenu>
在这里我通过 @HostListener
@HostListener('click',['$ event'])
然后将 MatMenuTrigger
指令的逻辑复制到
这基本上是 openMenu()$的复制。
menu-trigger.ts
源代码中的c $ c>方法,除了在菜单初始化之后以及调用 this.menuTrigger.menu ['_ startAnimation']();
打开菜单。
我将源按钮的尺寸存储到变量中,然后
使用计算按钮的中心点,然后将其与初始化菜单的
宽度一起使用,以计算left
@Directive({
选择器:'[center-mat-menu]'
})
出口类CenterMatmenuDirective {
overlayRef:OverlayRef;
overlayConf:OverlayConfig;
dropDown:HTMLElement;
overlayPositionBox:HTMLElement;
菜单:MatMenuPanel;
按钮:HTMLElement;
buttonWidth:数字;
buttonLeft:数字;
按钮底部:数字;
@Input(’center-mat-menu’)私人菜单触发:MatMenuTrigger;
构造函数(私有_menuButton:ElementRef,私有_renderer:Renderer2){}
@HostListener('click',['$ event'])
onclick( e){
this._setVariables();
//菜单不是通过键盘的向下箭头打开的,必须进行设置,以便MatMenuTrigger知道菜单是通过鼠标单击打开的。
this.menuTrigger [’_ openedBy’] = e.button === 0吗? mouse:null;
this._overrideMatMenu();
this.dropDown = this.overlayRef.overlayElement.children [0] .children [0]作为HTMLElement;
this.overlayPositionBox = this.overlayRef.hostElement;
setTimeout(()=> {
this._styleDropDown(this.dropDown);
this._setOverlayPosition(this.dropDown,this.overlayPositionBox);
this._openMenu();
})
}
private _setVariables(){
const config = this.menuTrigger ['_ getOverlayConfig']();
this.menuTrigger ['_ overlayRef'] = this.menuTrigger ['_ overlay']。create(config);
this.overlayRef = this.menuTrigger [’_ overlayRef’];
this.overlayConf = this.overlayRef.getConfig();
this.overlayRef.keydownEvents()。subscribe();
this.menu = this.menuTrigger.menu;
this._setButtonVars();
}
private _setButtonVars(){
this.button = this._menuButton.nativeElement;
this.buttonWidth = this.button.getBoundingClientRect()。width;
this.buttonLeft = this.button.getBoundingClientRect()。left;
this.buttonBottom = this.button.getBoundingClientRect()。bottom;
}
private _overrideMatMenu(){
let strat = this.overlayConf.positionStrategy as FlexibleConnectedPositionStrategy;
this.menuTrigger ['_ setPosition'](strat);
strat.positionChanges.subscribe(()=> {
this._setButtonVars();
this._setOverlayPosition(this.dropDown,this.overlayPositionBox);
})
this.overlayConf.hasBackdrop = this.menu.hasBackdrop == null吗?
!this.menuTrigger.triggersSubmenu():this.menu.hasBackdrop;
this.overlayRef.attach(this.menuTrigger [’_ getPortal']());
if(this.menu.lazyContent){
this.menu.lazyContent.attach()
}
this.menuTrigger ['_ closeSubscription' ] = this.menuTrigger ['_ menuClosingActions']()。subscribe(()=> {
this.menuTrigger.closeMenu();
});
this.menuTrigger [’_ initMenu’]();
}
private _styleDropDown(dropDown:HTMLElement){
this._renderer.setStyle(this._renderer.parentNode(dropDown),'transform-origin','center top 0px ');
}
private _setOverlayPosition(dropDown:HTMLElement,overlayPositionBox:HTMLElement){
let dropDownleft =((this.buttonWidth / 2 + this.buttonLeft)-dropDown.offsetWidth / 2 );
this._renderer.setStyle(overlayPositionBox,'top',this.buttonBottom + 5 +'px');
this._renderer.setStyle(overlayPositionBox,'left',dropDownleft +'px');
this._renderer.setStyle(overlayPositionBox,'height','100%');
}
private _openMenu(){
this.menuTrigger.menu [’_ startAnimation’]();
}
}
Per Material Issue 9631 centering the mat-menu
to the button is as follows.
the issue is that supporting it deviates from the spec and may end up looking weird.
I have a need for this functionality... Because writing my own CDK overlay would be more time intensive than overriding the mat-menu
component... I am not interested in re-creating the mat-menu
, and simply need the menu to be centered... I am also not interested in any other library's to accomplish this, I want to use Material mat-menu
, so my question is as follows.
Question:
Utilizing an angular directive
, how can I override the private variables and methods of the MatMenuTrigger
to center the CDK overlay to the button?
I chose a Directive because MatMenuTrigger
is a Directive
, it made sense to replicate the logic used by the Material source.
So far this is the approach I have come up with, I am not sure if reaching into the class like this is "acceptable" per se, or if there are any negative ramifications for doing it this way.
I am open to constructive discussion on this approach for any recommendations or improvements.
Stackblitz
https://stackblitz.com/edit/angular-jpgjdc-nqmyht?embed=1&file=app/center-matmenu.directive.ts
Essentially I am decoupling the matMenuTrigger
from the button by placing it on a div
wrapping the mat-menu
... this is so I can programmatically open the menu from the directive and not the button.
- I am also assigning
menuTrigger
to atemplateRef
on thediv
and passing it as an input to mycenter-mat-menu
selector
<button mat-button [center-mat-menu]="menuTrigger">Menu</button> <div #menuTrigger="matMenuTrigger" [matMenuTriggerFor]="menu"> <mat-menu #menu="matMenu">
From there I am creating a listener via @HostListener
@HostListener('click', ['$event'])
And then replicating the logic of the MatMenuTrigger
directive to manipulation the placement, initialize the menu, and fire the open animation on click.
This is basically a replication of the openMenu()
method in the menu-trigger.ts
source except I am manipulating the left and top styles after the menu is initialized, and before I call this.menuTrigger.menu['_startAnimation']();
to open the menu.
I store the dimension of the source button into variables and use that information to calculate the center point of the button, I then use that with the width of the initialized menu to calculate
left
@Directive({
selector: '[center-mat-menu]'
})
export class CenterMatmenuDirective {
overlayRef: OverlayRef;
overlayConf: OverlayConfig;
dropDown: HTMLElement;
overlayPositionBox: HTMLElement;
menu: MatMenuPanel;
button: HTMLElement;
buttonWidth: number;
buttonLeft: number;
buttonBottom: number;
@Input('center-mat-menu') private menuTrigger: MatMenuTrigger;
constructor(private _menuButton: ElementRef, private _renderer: Renderer2) { }
@HostListener('click', ['$event'])
onclick(e) {
this._setVariables();
//menu not opened by keyboard down arrow, have to set this so MatMenuTrigger knows the menu was opened with a mouse click
this.menuTrigger['_openedBy'] = e.button === 0 ? 'mouse' : null;
this._overrideMatMenu();
this.dropDown = this.overlayRef.overlayElement.children[0].children[0] as HTMLElement;
this.overlayPositionBox = this.overlayRef.hostElement;
setTimeout(() => {
this._styleDropDown(this.dropDown);
this._setOverlayPosition(this.dropDown, this.overlayPositionBox);
this._openMenu();
})
}
private _setVariables() {
const config = this.menuTrigger['_getOverlayConfig']();
this.menuTrigger['_overlayRef'] = this.menuTrigger['_overlay'].create(config);
this.overlayRef = this.menuTrigger['_overlayRef'];
this.overlayConf = this.overlayRef.getConfig();
this.overlayRef.keydownEvents().subscribe();
this.menu = this.menuTrigger.menu;
this._setButtonVars();
}
private _setButtonVars() {
this.button = this._menuButton.nativeElement;
this.buttonWidth = this.button.getBoundingClientRect().width;
this.buttonLeft = this.button.getBoundingClientRect().left;
this.buttonBottom = this.button.getBoundingClientRect().bottom;
}
private _overrideMatMenu() {
let strat = this.overlayConf.positionStrategy as FlexibleConnectedPositionStrategy;
this.menuTrigger['_setPosition'](strat);
strat.positionChanges.subscribe(() => {
this._setButtonVars();
this._setOverlayPosition(this.dropDown, this.overlayPositionBox);
})
this.overlayConf.hasBackdrop = this.menu.hasBackdrop == null ?
!this.menuTrigger.triggersSubmenu() : this.menu.hasBackdrop;
this.overlayRef.attach(this.menuTrigger['_getPortal']());
if (this.menu.lazyContent) {
this.menu.lazyContent.attach()
}
this.menuTrigger['_closeSubscription'] = this.menuTrigger['_menuClosingActions']().subscribe(() => {
this.menuTrigger.closeMenu();
});
this.menuTrigger['_initMenu']();
}
private _styleDropDown(dropDown: HTMLElement) {
this._renderer.setStyle(this._renderer.parentNode(dropDown), 'transform-origin', 'center top 0px');
}
private _setOverlayPosition(dropDown: HTMLElement, overlayPositionBox: HTMLElement) {
let dropDownleft = ((this.buttonWidth / 2 + this.buttonLeft) - dropDown.offsetWidth / 2);
this._renderer.setStyle(overlayPositionBox, 'top', this.buttonBottom + 5 + 'px');
this._renderer.setStyle(overlayPositionBox, 'left', dropDownleft + 'px');
this._renderer.setStyle(overlayPositionBox, 'height', '100%');
}
private _openMenu() {
this.menuTrigger.menu['_startAnimation']();
}
}
这篇关于中心Mat-Menu,中心MatMenu与按钮叠加的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!