Динамически вложенное угловое меню материалов

Подскажите, пожалуйста, как я могу решить следующую проблему: Мне нужно реализовать динамически создаваемое меню с разными уровнями вложенности в зависимости от объекта модели данных. На данный момент с помощью рекурсии нам удалось создать меню как таковое, однако существует проблема с назначением атрибута [matMenuTriggerFor] непосредственно для подменю. Проблема в том, что все последующие подменю на самом деле относятся к самому первому, поэтому при наведении курсора на любое из подменю происходит «переход» к исходному (пример на изображении: меню, которое включает элементы: Устройство, Расширение, Очередь, Член очереди (с элементами подменю)). Таким образом, на долю секунды я вижу другой кадр подменю (пример на изображении: подменю Сгруппированный список), после чего становится активным самый первый. Конечно, может, я не все сделал правильно, поэтому обращаюсь сюда. Помоги мне, пожалуйста. Спасибо вам всем.

Динамически вложенное угловое меню материалов

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>
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
4
0
10 977
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

В итоге получилось, опираясь на несколько аналогичных проблем с другими. Разобраться помогли примеры из ЗДЕСЬ (пример динамического вложенного меню) и из ЗДЕСЬ (проблема с мат-меню скрывается сразу при открытии) (в последнем примере достаточно было просто обновить zone.js на npm)

Извините за поздний ответ, но, возможно, вы все же найдете его полезным. Я написал небольшую библиотеку под названием ng-action-outlet, которая, на мой взгляд, делает это довольно аккуратно.


Выглядит это так:

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>

ДЕМО: https://stackblitz.com/edit/ng-action-outlet-demo?file=src/app/app.component.ts

Спасибо за ответ. Сейчас сам вопрос не особо актуален, но в дальнейшем постараюсь акцентировать внимание на вашем варианте)

titaniche 14.06.2019 17:22

Другие вопросы по теме