Sikha Rani Mohapatra
Sikha Rani Mohapatra

Reputation: 41

Angular : How to create a dynamic menu in angular from database data (Asp net core Web API data)

I'm working with angular 11. I don't have any idea of creating a dynamic menu in angular.

Actually I have created two tables in database (i.e MainMenu and subMenu). I have to create a aspnet core web api in .net Core for that and want to bind aspnet core web api data into angular [means want to create a dynamic menu angular from aspnet core web api data].

Upvotes: 4

Views: 7990

Answers (1)

Eliseo
Eliseo

Reputation: 57979

It's a broad question, and you give us few clues. So I simply indicate a "general method".

1.-Look for a menu/submenu in internet you like,e.g. I get the first I found in this link (the first, I found). You can look for a side-nav menu or waht you want that like it. In general it's was in the way:

<div id="header">
  <ul class="nav">
    <li><a href="">Home</a></li>
    <li><a href="">Services</a>
      <ul>
        <li><a href="">Submenu1</a></li>
        <li><a href="">Submenu2</a></li>
        <li><a href="">Submenu3</a></li>
        <li><a href="">Submenu4</a>
          <ul>
            <li><a href="">Submenu1</a></li>
            <li><a href="">Submenu2</a></li>
            <li><a href="">Submenu3</a></li>
            <li><a href="">Submenu4</a></li>
          </ul>
        </li>
      </ul>
    </li>
    <li><a href="">About</a>
      <ul>
        <li><a href="">Submenu1</a></li>
        <li><a href="">Submenu2</a></li>
        <li><a href="">Submenu3</a></li>
        <li><a href="">Submenu4</a></li>
      </ul>
    </li>
    <li><a href="">Contact</a></li>
  </ul>
</div>

2.-Thinks on variables, arrays and object. and try to recreate using variables

You see that a menu "item" has a label, and href, so is more or less {label:"...",href:"..",children:[...]} and yes, the children are simples arrays of this object. Imagine some like

  menu=[
    {href:"",label:"Home"},
    {href:"",label:"Servicios",children:[
        {href:"",label:"Submenu1"},
        {href:"",label:"Submenu2"},
        {href:"",label:"Submenu3"},
        {href:"",label:"Submenu4",children:[
            {href:"",label:"Submenu1"},
            {href:"",label:"Submenu2"},
            {href:"",label:"Submenu3"},
            {href:"",label:"Submenu4"}
        ]},
    ]},
    {href:"",label:"About",children:[
        {href:"",label:"Submenu1"},
        {href:"",label:"Submenu2"},
        {href:"",label:"Submenu3"},
        {href:"",label:"Submenu4"}
        ]},
    {href:"",label:"Contact"}                                       
    ]

We can imagine so display this object usign *ngFor and *ngIf. In Angular we not use href else routerLink

<div id="header">
  <ul class="nav">
    <li *ngFor="let item of menu">
      <a [routerLink]="item.href">{{item.label}}</a>
      <ul *ngIf="item.children">
        <li *ngFor="let submenu of item.children">
          <a [routerLink]="submenu.href">{{submenu.label}}</a>
          <ul *ngIf="submenu.children">
            <li *ngFor="let submenu2 of submenu.children">
              <a [routerLink]="submenu2.href">{{submenu2.label}}</a>
            </li>
          </ul>
        </li>
      </ul>
    </li>
  </ul>
</div>

We can think in make a "recursive" component or conform with this "three levels". Make a recursive component is not difficult, you can see this SO

@Component({
  selector: '[recursive]',
  template: `
  <li *ngFor="let item of children">
    <a [routerLink]="item.href">{{item.label}}</a>
    <ul recursive *ngIf="item.children" 
        [children]="item.children" 
        [parent]="self" 
        [level]="level!=undefined?level+1:0">
    </ul>
</li>
  `,
})
export class MenuItemComponent  {
  @Input() level: number;
  @Input() label: string;
  @Input() children: any[];
  @Input() parent: any;

  self=this;
}

And our .html like

<div id="header">
   <ul class="nav" recursive [children]="menu"></ul>
</div>

3.-Create the object. We are ready to display a menu, now it's time to create the object "menu" using the tables

As always, tipical we has a service that get the table menu and the table submenu

/*Imagine return an array like
 [{id:1,label:...,href:...},
  {id:2,label:...,href:...}..]
*/
getMenu()
{
   return this.http.get("myApi/menu");
}

/*Imagine return an array like
 [{id:1,label:...,href:...,menuId:1},
  {id:2,label:...,href:...,menuId:1}..]
*/
getSubMenu()
{
   return this.http.get("myApi/submenu");
}

So we can create a new functions that get the two tables and return our object "menu"

  getObjectMenu() {
    return forkJoin([this.getMenu(), this.getSubMenu()]).pipe(
      map(([menu, submenu]: [any[], any[]]) => {
        return menu.map((x: any) => ({
          ...x,
          children: submenu.filter(s => s.menuId == x.id)
        }));
      })
    );
  }

see that forkJoin create an observable based in an array of observable. We get the two tables in "menu,submenu" and create -using map- a new object that is the table "menu" plus an addicional property "children" that contains the submenus who menuId==x.id

4.-Using our service in the Angular app. Well, now, we can in a ngOnInit subscribe to service and give value to a variable

constructor(private menuService:MenuService){}
ngOnInit()
{
  this.menuService.getObjectMenu().subscribe(res=>{
    this.menuData=res
  })
}

And use

<div id="header">
   <ul class="nav" recursive [children]="menuData"></ul>
</div>

Or, as our service.getObjectMenu() return an observable, we can use pipe async

<div id="header">
  <ul class="nav">
    <!--see that use menuService.getObjectMenu()|async-->
    <li *ngFor="let item of menuService.getObjectMenu()|async">
      <a [routerLink]="item.href">{{item.label}}</a>
      <ul *ngIf="item.children">
      ...
  </ul>
</div>

//or using the recursive

<div id="header">
   <ul class="nav" recursive [children]="menuService.getObjectMenu()|async">
   </ul>
</div>

Update well, really is a "bad desing" to get a table of "menus" and "submenus",

5.-Generally you only get an unique table each element in the way { id: .., href:.., label: .., menuId: .. }

if menuId=0 is a menu, else is a submenu. So we can imagine a function

getAllMenu()()
{
   return this.http.get("myApi/menu");
}

How create our object "menuObject". As is a recursive you need remember that you should has a function that is called itself. Well, e can has a function

  getObjectMenu() {
    return this.getAllMenu().pipe(
      map((menu: any[]) =>
        menu.filter(x=>!x.menuId).map(x => ({
          ...x, 
          children: this.getChildren(menu,x.id) //<--here call to function
        }))
      )
    );
  }

  getChildren(menu:any[],id:number){
     return menu.filter(x=>x.menuId==id).map((x:any)=>{
       return ({
       ...x,
       children:this.getChildren(menu,x.id) //<--see the function is called to itself

     })}
     )
  }

See that all our angular application works, the only is change the "service"

See a stackblitz

Upvotes: 8

Related Questions