Dynamically nested angular material menu
Solution 1
As a result, it turned out, relying on several similar problems with others. The examples from HERE (dynamic nested menu example) and from HERE (the problem with mat-menu hides immediately on opening) helped to figure it out (in the last example it was enough just to update zone.js
by npm
)
Solution 2
Sorry for the late answer, but maybe you can still find it helpful.
I wrote a little library called ng-action-outlet
that is doing that quite neatly in my opinion.
It looks like this:
group: ActionGroup;
constructor(private actionOutlet: ActionOutletFactory) {
this.group = this.actionOutlet.createGroup();
this.group.createButton().setIcon('home').fire$.subscribe(this.callback);
this.group.createButton().setIcon('settings').fire$.subscribe(this.callback);
}
<ng-container *actionOutlet="group"></ng-container>
DEMO: https://stackblitz.com/edit/ng-action-outlet-demo?file=src/app/app.component.ts
titaniche
Updated on June 08, 2022Comments
-
titaniche about 2 years
Please tell me how I can solve the following problem: I need to implement a dynamically created menu with different nesting levels depending on the data model object. At the moment, using recursion, we managed to create the menu as such, however, there is a problem of assigning the attribute [matMenuTriggerFor] for, directly, the submenu. The problem is that all subsequent submenus in fact refer to the very first, so when you hover over any of the submenus, it causes a "flip" to the original one (example on image: menu, which includes elements: Device, Extension, Queue, Queue member (with submenu elements)). Thus, for a fraction of seconds, I see the other submenu frame (example on image: submenu Grouped list), after which the very first becomes active. Of course, maybe I didn’t do everything right, so I’m turning here. Help me please. Thank you all.
imenu-item.ts
export interface IMenuItem { name: string | string[]; link: string; subItems: IMenuItem[]; }
dynamic-menu.service.ts
import {Inject, Injectable} from '@angular/core'; import {APP_CONFIG_ROUTES} from '../../../config/routes/app.config.routes'; import {IAppConfigRoutes} from '../../../config/routes/iapp.config.routes'; import {IMenuItem} from './imenu-item'; import {_} from '@biesbjerg/ngx-translate-extract/dist/utils/utils'; @Injectable({ providedIn: 'root' }) export class DynamicMenuService { private readonly appConfig: any; constructor(@Inject(APP_CONFIG_ROUTES) appConfig: IAppConfigRoutes) { this.appConfig = appConfig; } getMenuItems(): IMenuItem[] { return [ { name: _('labels.device'), link: '/' + this.appConfig.routes.device, subItems: null }, { name: _('labels.extension'), link: '/' + this.appConfig.routes.extension, subItems: null }, { name: _('labels.queue'), link: '/' + this.appConfig.routes.queue, subItems: null }, { name: _('labels.queueMember'), link: null, subItems: [{ name: _('labels.fullList'), link: '/' + this.appConfig.routes.queueMember.all, subItems: null }, { name: _('labels.groupedList'), link: '/' + this.appConfig.routes.queueMember.grouped, subItems: [{ name: 'subName', link: 'subLink', subItems: [{ name: 'subSubName1', link: 'subSubLink1', subItems: null }, { name: 'subSubName2', link: 'subSubLink2', subItems: null }] }] }] } ]; } }
dynamic-menu.component.ts
import {Component, Input, OnInit} from '@angular/core'; import {IMenuItem} from './imenu-item'; @Component({ selector: 'app-dynamic-menu', templateUrl: './dynamic-menu.component.html', styleUrls: ['./dynamic-menu.component.scss'] }) export class DynamicMenuComponent implements OnInit { dynamicMenuItemsData: IMenuItem[]; constructor(private dynamicMenuService: DynamicMenuService) { } ngOnInit() { this.dynamicMenuItemsData = this.dynamicMenuService.getMenuItems(); } }
dynamic-menu.component.html
<div> <ng-container [ngTemplateOutlet]="recursiveListMenuItems" [ngTemplateOutletContext]="{$implicit: dynamicMenuItemsData}"> </ng-container> </div> <ng-template #recursiveListMenuItems let-listMenuItems> <div *ngFor="let menuItem of listMenuItems"> <ng-container [ngTemplateOutlet]="menuItem.subItems != null ? subMenuItem : simpleMenuItem" [ngTemplateOutletContext]="{$implicit: menuItem}"> </ng-container> </div> </ng-template> <ng-template #simpleMenuItem let-menuItemArg> <a class="mat-button" mat-menu-item routerLink="{{menuItemArg.link}}"> <span>{{menuItemArg.name | translate}}</span> </a> </ng-template> <ng-template #subMenuItem let-menuItemArg> <a class="mat-button" mat-menu-item routerLink="{{menuItemArg.link}}" [matMenuTriggerFor]="subItemsMenu"> <span>{{menuItemArg.name | translate}}</span> <mat-menu #subItemsMenu="matMenu" [overlapTrigger]="false"> <ng-container [ngTemplateOutlet]="recursiveListMenuItems" [ngTemplateOutletContext]="{$implicit: menuItemArg.subItems}"> </ng-container> </mat-menu> </a> </ng-template>
-
titaniche about 5 yearsThanks for the answer. Now the question itself is not particularly relevant, but in the future I will try to focus on your option)