Angular — Как создать многоразовый компонент матового меню
У меня есть матовое меню, которое нужно запускать в нескольких местах.
Где у меня есть повторяющиеся элементы списка (ngFor), и на каждом элементе списка мы показываем больше значков, и при нажатии на него отображается матовое меню с 3 параметрами, каждый из которых при нажатии вызывает одну функцию.
Здесь у меня есть кнопка, и при нажатии на нее отображается матовое меню с теми же тремя параметрами, каждый из которых при нажатии будет вызывать ту же функцию, что и выше.
Каждая функция по клику открывает мат-диалог с формой внутри него.
Наконец, у нас есть разные триггерные элементы меню, но одно и то же матовое меню.
В настоящее время я повторил код в компоненте, что является плохим способом сделать это. У меня много повторяющегося кода в обоих компонентах. Я хотел бы оптимизировать его Итак, как мы можем это сделать.
Могу ли я создать общий компонент для разных пунктов меню? (Возможно ли это, поскольку запуск элементов мат-меню отличается) Или я могу создать службу для вызова меню по функциям щелчка? Или я могу создать интерфейс и поместить туда функции реализации и реализовать этот интерфейс в каждом компоненте, где я использую матовое меню?
Пожалуйста, предложите мне, как я могу оптимизировать его.
1 ответ
Мы можем создать компонент «matmenu», который может питаться массивом объектов.
Представьте, что у вас есть массив, например
menu:any[]=[
{label:"Vertebrates",children:[
{label:"Fishes",children:[
{label:"Baikal oilfish",action:1},
{label:"Bala shark",action:2},
]},
{label:"Amphibians",children:[
{label:"Sonoran desert toad",action:3},
{label:"Western toad",action:4},
{label:"Arroyo toad",action:5},
{label:"Yosemite toad",action:6},
]},
{label:"Reptiles",action:7},
{label:"Birds",action:8},
{label:"Mammals",action:9}
]
},
{label:"Invertebrates",children:[
{label:"Insects",action:10},
{label:"Molluscs",action:11},
{label:"Crustaceans",action:12}
]
}
]
Вы можете создать компонент, который создает "мат-меню"
export class MenuComponent implements OnInit,AfterViewInit {
@Input() title:string
@Input() menu:any[]
@Output() action:EventEmitter<any>=new EventEmitter<string>()
//we are going to get all "mat-menu" using viewChildren
@ViewChildren(MatMenu) matmenus:QueryList<MatMenu>
menuItems:any[]=[]
yet:boolean=false;
submenus:any[]=[]
ngOnInit(){
this.createSubmenus(this.menu,"s0",1)
this.reindex()
}
ngAfterViewInit(){
//this avoid Angular give us errors, only when repaint the menu
//we asign the [matMenutiggerFor]
setTimeout(()=>{
this.yet=true
})
}
//simply call to the output
onClick(value:any)
{
this.action.emit(value);
}
//return the "mat-menu" in the index selected
getMenu(index:number)
{
return index>=0 && this.matmenus?this.matmenus.find((x,i)=>i==index):null
}
reindex(){
//asign the "index" of the menu item
this.submenus.forEach(menu=>{
menu.forEach((x:any)=>{
if (x.subMenu!=-1)
x.subMenu=this.menuItems.indexOf(x.action)
})
})
}
createSubmenus(menu:any[],prefix:string,count:number){
//add to the array menuItems the "prefix"
this.menuItems.push(prefix)
//add to submenu an object to create the submenu
this.submenus.push(menu.map((x:any,index:number)=>(
{
label:x.label,
action:x.children===null || x.children===undefined?x.action:prefix+index,
subMenu:x.children===null || x.children===undefined?-1:0
}
)))
//if has children call the function for each child
menu.forEach((x:any,index:number)=>{
if (x.children){
this.createSubmenus(x.children,prefix+index,count+1)
}
})
}
}
С .html
<ng-container *ngIf="yet">
<button mat-button [matMenuTriggerFor]="matmenus.first">
<ng-content></ng-content>
</button>
</ng-container>
<ng-container *ngFor="let menu of submenus">
<mat-menu>
<ng-container *ngFor="let item of menu">
<button mat-menu-item *ngIf="item.subMenu!=-1 && yet"
[matMenuTriggerFor]="getMenu(item.subMenu)">
{{item.label}}
</button>
<button mat-menu-item *ngIf="item.subMenu==-1" (click)="onClick(item.action)">
{{item.label}}
</button>
</ng-container>
</mat-menu>
</ng-container>
Вы можете увидеть в этом stackblitz . Увидите, что вам нужно только накормить menuComponent массивом, создав функцию:
action(value:any)
{
console.log(value)
}
И работать как
<menu-component [menu]="menu" (action)="action($event)">
Animals
</menu-component>