I know it's supposed to be simple but I'm trying to find a nice way to do the following:
I have a menu in my sidebar and I want that by clicking on a specific span it will not rout me somewhere but will show me another menu within it. Below is the code:
sidebar.html
<div *ngFor="let item of items">
<span>{{item.label}}
<div *ngIf="isClicked" *ngFor="let nestedItem of item.nestedItems" class="nav-nestedItem-label">
{{nestedItem.label}}
</div>
sidebar.ts
this.items = [
{ label: 'A', image: 'dashboard', route: 'AAA' },
{ label: 'B', image: 'my-apps', route: 'BBB' },
{ label: 'C', image: 'app-store', route: 'CCC' },
{ label: 'D', image: 'data', route: 'DDD' },
{ label: 'E', image: 'commercial-tools', route: 'EEE' },
{ label: 'F', image: 'dev-tools', route: 'FFF', },
{ label: 'G', image: 'data-source-mng', route: '',
nestedItems: [{label: 'GA'},
{label: 'GB'},
{label: 'GC'}]},
];
I want the nestedItems to open for me by pressing lable 'G' and it will nor rout to anywhere.
He is currently showing me the entire menu including the nestedItems
How can this be arranged in a beautiful and non-primitive way? I did some way but the code is long and cumbersome. I need a beautiful way
Thank you!!!
CodePudding user response:
You can try something like this:
items = [
{ label: 'A', image: 'dashboard', route: 'AAA' },
{ label: 'B', image: 'my-apps', route: 'BBB' },
{ label: 'C', image: 'app-store', route: 'CCC' },
{ label: 'D', image: 'data', route: 'DDD' },
{ label: 'E', image: 'commercial-tools', route: 'EEE' },
{ label: 'F', image: 'dev-tools', route: 'FFF', },
{ label: 'G', image: 'data-source-mng', route: '',
nestedItems: [{label: 'GA', route: 'GAAA'},
{label: 'GB', route: 'GBBB'},
{label: 'GC', route: 'GCCC'}]},
];
myClick(item: any, event?: any): void {
if (item.nestedItems) {
event.target.querySelector('div').hidden = false;
} else {
// redirect to...
}
}
and your html:
<div *ngFor="let item of items" (click)="myClick(item, $event)">
<span>{{item.label}}
<div hidden="true" *ngIf="item.nestedItems" class="nav-nestedItem-label">
<div *ngFor="let nestedItem of item.nestedItems" (click)="myClick(nestedItem)">
> {{nestedItem.label}}
</div>
</div>
</span>
</div>
CodePudding user response:
I would store the clicked menu item in a property. You can then use *ngIf
to compare the loop item
with the clicked item to decide whether or not to show the menu item.
component.html
<div *ngFor="let item of items" (click)="expand(item)">
<span>{{item.label}}</span>
<ng-container *ngIf="item === expanded">
<div *ngFor="let nestedItem of item.nestedItems">
{{nestedItem.label}}
</div>
</ng-container>
</div>
(click)="expand(item)"
handles click events on the outer div, passing in the current item
as the single argument.
<ng-container>
is an angular component that doesn't get rendered (also, you can't use both *ngIf
and *ngFor
on the same element)
*ngIf="item === expanded"
only renders the <ng-container>
when the expanded
item is the same object as the current item
.
component.ts
items: Item[] = [
{ label: 'A', image: 'dashboard', route: 'AAA' },
{ label: 'B', image: 'my-apps', route: 'BBB' },
{ label: 'C', image: 'app-store', route: 'CCC' },
{ label: 'D', image: 'data', route: 'DDD' },
{ label: 'E', image: 'commercial-tools', route: 'EEE' },
{
label: 'F',
image: 'dev-tools',
nestedItems: [{ label: 'FA', route: 'FAAA' }],
},
{
label: 'G',
image: 'data-source-mng',
nestedItems: [
{ label: 'GA', route: 'GAAA' },
{ label: 'GB', route: 'GBBB' },
{ label: 'GC', route: 'GCCC' },
],
},
];
expanded?: Item;
expand(item: Item): void {
this.expanded = item;
}
I have created 2 interfaces as below. You could also declare expanded: any;
without using interfaces, but my personal preference is to use types where possible.
interface Item {
label: string;
image?: string;
route?: string;
nestedItems?: SubItem[];
}
interface SubItem {
label: string;
route: string;
}
Outside of scope: collapsing the menu item when elements outside of the menu are clicked.
Stackblitz Demo: https://stackblitz.com/edit/angular-ivy-f1kifq?file=src/app/app.component.html