Angular — Как создать многоразовый компонент матового меню

У меня есть матовое меню, которое нужно запускать в нескольких местах.

  1. Где у меня есть повторяющиеся элементы списка (ngFor), и на каждом элементе списка мы показываем больше значков, и при нажатии на него отображается матовое меню с 3 параметрами, каждый из которых при нажатии вызывает одну функцию.

  2. Здесь у меня есть кнопка, и при нажатии на нее отображается матовое меню с теми же тремя параметрами, каждый из которых при нажатии будет вызывать ту же функцию, что и выше.

Каждая функция по клику открывает мат-диалог с формой внутри него.

Наконец, у нас есть разные триггерные элементы меню, но одно и то же матовое меню.

В настоящее время я повторил код в компоненте, что является плохим способом сделать это. У меня много повторяющегося кода в обоих компонентах. Я хотел бы оптимизировать его Итак, как мы можем это сделать.

Могу ли я создать общий компонент для разных пунктов меню? (Возможно ли это, поскольку запуск элементов мат-меню отличается) Или я могу создать службу для вызова меню по функциям щелчка? Или я могу создать интерфейс и поместить туда функции реализации и реализовать этот интерфейс в каждом компоненте, где я использую матовое меню?

Пожалуйста, предложите мне, как я могу оптимизировать его.

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>
Другие вопросы по тегам