I'm trying to create a mat-nav-list which has mat-expansion-panels as list-items.
The problem is, the mat-nav-list doesn't work as expected.The alignments are a mess and the expansion panels do not work properly. As I think, this happens because the mat-nav-lists don't support mat-expansion-panels as list items.(Correct me if I'm wrong)
My html code is as following
<mat-nav-list>
<mat-list-item>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
Personal data
</mat-panel-title>
<mat-panel-description>
Type your name and age
</mat-panel-description>
</mat-expansion-panel-header>
<mat-form-field>
<input matInput placeholder="First name">
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Age">
</mat-form-field>
</mat-expansion-panel>
</mat-list-item>
<mat-list-item>
<mat-expansion-panel (opened)="panelOpenState = true"
(closed)="panelOpenState = false">
<mat-expansion-panel-header>
<mat-panel-title>
Self aware panel
</mat-panel-title>
<mat-panel-description>
Currently I am {{panelOpenState ? 'open' : 'closed'}}
</mat-panel-description>
</mat-expansion-panel-header>
<p>I'm visible because I am open</p>
</mat-expansion-panel>
</mat-list-item>
</mat-nav-list>
I will be thankful if someone can provide me a way to deal with this issue.
Instead of using mat-list-item you can have a div on for loop is iterated and in that you can add the expansion panel likewise
<div *ngFor="let hero of herolist">
<mat-expansion-panel>
// here you can do whateever you want
</mat-expansion-panel>
</div>
If you want to achieve it with sidenav, you can have it
<ng-container>
<mat-side-nav>
</mat-side-nav>
</ng-container>
Without a lot of hacking mat-expansion-panel
does not work properly with mat-nav-list
.
However, mat-tree
does work nicely with it.
You can combine them as follows. Also see this Stackblitz example that I created.
HTML:
<mat-nav-list>
<mat-tree [dataSource]="menuItems" [treeControl]="menuTreeControl">
<mat-tree-node *matTreeNodeDef="let menuItem">
<a
[routerLink]="menuItem.link"
mat-list-item
routerLinkActive="mdc-list-item--activated"
>
{{ menuItem.name }}
</a>
</mat-tree-node>
<mat-nested-tree-node *matTreeNodeDef="let menuItem; when: hasChild">
<a mat-list-item matTreeNodeToggle>
<span matListItemTitle
>{{ menuItem.name }}
<mat-icon>
{{
menuTreeControl.isExpanded(menuItem)
? "expand_more"
: "expand_less"
}}
</mat-icon>
</span>
</a>
<div *ngIf="menuTreeControl.isExpanded(menuItem)" role="group">
<ng-container matTreeNodeOutlet></ng-container>
</div>
</mat-nested-tree-node>
</mat-tree>
</mat-nav-list>
SCSS:
mat-nav-list {
div[role="group"] {
padding-left: 20px;
}
mat-icon {
position: absolute;
right: 20px;
}
}
Typescript
export class NavMenuComponent {
previewEnabled = Cookies.get("previewEnabled") === "true";
title$: Observable<string>;
menuTreeControl = new NestedTreeControl<MenuItem>(
(menuItem) => menuItem.children
);
menuItems: MenuItem[] = [
{ type: "link", link: "link-1", name: "Item 1"},
{
type: "section",
name: "Parent Item",
children: [
{ type: "link", link: "link-2", name: "Item 2" },
{ type: "link", link: "link-3", name: "Item 3" },
],
},
];
constructor(
private router: Router,
) {
router.events
.pipe(filter((event) => event instanceof NavigationEnd))
.subscribe(() => this.openActiveRouteParentMenuItem());
}
openActiveRouteParentMenuItem() {
_.forEach(this.menuItems, (parentMenuItem) => {
const childMenuItems = this.menuTreeControl.getChildren(
parentMenuItem
) as MenuItem[];
_.forEach(childMenuItems, (childMenuItem) => {
if (
this.router.isActive(childMenuItem.link as string, {
paths: "subset",
queryParams: "subset",
fragment: "ignored",
matrixParams: "ignored",
})
) {
this.menuTreeControl.expand(parentMenuItem);
}
});
});
}
hasChild = (_: number, menuItem: MenuItem) => !!menuItem.children?.length;
}
interface MenuItem {
type: "link" | "section";
name: string;
link?: string;
children?: MenuItem[];
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With