Reputation: 41
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
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