嗯,还有另一种方法是使用 cdk 制作我们自己的“菜单”。先看看SO I wrote in comments
要打开菜单,我们将考虑两个元素,“原点”(被点击的 div)和“锚点”,即叠加层要附加的元素
代码是这样的
constructor(
private overlay: Overlay,
private viewContainerRef: ViewContainerRef
) {}
openMenu(origin: any, anchor: any, menu: any) {
this.close(null);
this.overlayRef = this.overlay.create(this.getOverlayConfig(anchor,origin));
this.overlayRef.attach(
new TemplatePortal(menu, this.viewContainerRef, {
$implicit: this
})
);
setTimeout(() => {
this.sub = fromEvent<MouseEvent>(document, "click")
.pipe(
filter(event => {
const clickTarget = event.target as HTMLElement;
return (
clickTarget != origin &&
(!!this.overlayRef &&
!this.overlayRef.overlayElement.contains(clickTarget))
);
}),
take(1)
)
.subscribe(() => {
this.close(null);
});
});
}
close = (data: any) => {
this.sub && this.sub.unsubscribe();
if (this.overlayRef) {
this.overlayRef.dispose();
this.overlayRef = null;
}
};
change(value: any, isChecked: boolean) {
if (!isChecked)
this.value = (this.value || []).filter(
(x: any) => x != value
);
if (isChecked)
this.value = this.toppingList.filter(
(x: any) =>
x == value || (this.value && this.value.indexOf(x) >= 0)
);
//when some change, we reposition the overLayRef
setTimeout(() => {
this.overlayRef && this.overlayRef.updatePosition();
});
}
private getOverlayPosition(origin: any): PositionStrategy {
const positionStrategy = this.overlay
.position()
.flexibleConnectedTo(origin)
.withPositions(this.getPositions())
.withPush(false);
return positionStrategy;
}
private getOverlayConfig(anchor: any,origin:any): OverlayConfig {
return new OverlayConfig({
hasBackdrop: false,
backdropClass: "popover-backdrop",
positionStrategy: this.getOverlayPosition(anchor),
scrollStrategy: this.overlay.scrollStrategies.close(),
width:origin.getBoundingClientRect().width,
panelClass:"mat-elevation-z8"
//you can add anothers properties, see
//https://material.angular.io/cdk/overlay/api#OverlayConfig
});
}
private getPositions(): ConnectionPositionPair[] {
return [
{
originX: "center",
originY: "bottom",
overlayX: "center",
overlayY: "top"
},
{
originX: "center",
originY: "top",
overlayX: "center",
overlayY: "bottom"
}
];
}
还有.html
<div class="container-all">
<div #field class="container-input" (click)="openMenu(field,anchor,tpl)">
<div>
<span>{{value || []}}</span>
</div>
<mat-icon matSuffix>keyboard_arrow_down</mat-icon>
</div>
<div #anchor [ngClass]="{'chip-list':value && value.length}">
<mat-chip-list aria-label="Fruit selection">
<mat-chip *ngFor="let fruit of value" removable="true" (removed)="change(fruit,false)">
{{fruit}}
<mat-icon matChipRemove>cancel</mat-icon>
</mat-chip>
</mat-chip-list>
</div>
</div>
<ng-template #tpl>
<div class="checkbox-list">
<div *ngFor="let topping of toppingList">
<mat-checkbox [checked]="value && value.indexOf(topping)>=0" (change)="change(topping,$event.checked)">
{{topping}}
</mat-checkbox>
</div>
</div>
</ng-template>
堆栈闪电战,像往常一样here
另一个看起来“更多材料”的堆栈闪电战here