'How can We create dynamic route navigation Link in angular with multi level children and which will recreate whole navigation on reload

I wanted to create a navigation list based on some configuration. like an Array of

export interface LeftNavModel {
name: string,
route: string,
iconName?: string,
data?: any }

so navigation data will look something like

[
  {
    name: "First Route",
    route:"first",
    sectionName: "First Section",
    iconName: "home",
    children: [
      {
        name: "First Child Route",
        route:`first/firstChild`,
        iconName: "folder"
        children: [
          {
            name: "First Sub Child Route",
            route:"first/firstChild/firstSubChild",
            iconName: "image"
          }
        ]
      }
    ]
  }
]

This data will create nav link like enter image description here

And in a component create routerLink using the routes from the config JSON. I was able to create the same and is working fine. The issue is I wanted to share the navigated URL so other users can open the same page with navigation created dynamically.

Page Refresh can be handled by keeping config as state in router. Or using any storage mechanism like localStorage. But how can we share the link along with config data. Passing config as queryParam will create huge url. Any other option for the same?



Solution 1:[1]

I had to do this in a project I am working on a while back.

I am using bootstrap accordions for my sidebar, and as such needed to give each menu item an id. You could accomplish the same thing with anything though doesn't have to be bootstrap.

Create a class to represent the menu/nav items (Mine is something like this)

export class MenuItem {
  id: number;
  text: string;
  icon: string;
  url?: string;
  children?: MenuItem[];
}

Create a menu items array which will store the list of items. This is a shortened version of what mine looks like:

export const menuItems = [
  {
    id: 1,
    text: 'Dashboard',
    icon: 'dashboard',
    url: 'dashboard',
  },
  {
    id: 2,
    text: 'Organizational Documents',
    icon: 'library_books',
    children: [
      {
        id: 3,
        text: 'View',
        icon: 'search',
        url: 'organizational-documents/view',
      },
    ],
  },
  {
    id: 4,
    text: 'Qualifications',
    icon: 'school',
    children: [
      {
        id: 5,
        text: 'Create',
        icon: 'create',
        url: 'qualifications/create',
      },
      {
        id: 6,
        text: 'View',
        icon: 'search',
        url: 'qualifications/view',
      },
    ],
  },
 {
    id: 7,
    text: 'Programmes',
    icon: 'library_books',
    children: [
      {
        id: 8,
        text: 'Create',
        icon: 'create',
        children: [
          {
            id: 9,
            text: 'Learnership',
            icon: 'add',
            url: 'programmes/create/learnership',
          },
          {
            id: 10,
            text: 'Internship',
            icon: 'add',
            url: 'programmes/create/internship',
          },
        ],
      },
{
        id: 11,
        text: 'View',
        icon: 'search',
        children: [
          {
            id: 12,
            text: 'Learnerships',
            icon: 'search',
            url: 'programmes/view/learnerships',
          },
          {
            id: 13,
            text: 'Internships',
            icon: 'search',
            url: 'programmes/view/internships',
          },
        ],
      },
]

Initialize the menu items from the component that contains the nav items

import { menuItems } from '../menuItems';

public ngOnInit() {
    this.renderMenu();
}

private renderMenu(): void {
  this.renderedMenuItems = JSON.parse(JSON.stringify(menuItems));
}

Add HTML similar to below in order to iterate over the menu items

 <nav id="sidebar" class="sidebar-open overflow-auto shadow">
    <div class="accordion accordion-flush mb-2" id="sidebarAccordion">
      <div *ngFor="let item of renderedMenuItems">
        <div *ngIf="!item.children">
          <a
            id="flush-{{ item.id }}"
            class="
              fs-6
              d-flex
              align-items-center
              p-2
              pt-3
              pb-3
              cursor-pointer
              non-dropdown
            "
            routerLink="{{ item.url }}"
            routerLinkActive="active-route-dash"
          >
            <mat-icon class="ms-1 me-3">{{ item.icon }}</mat-icon>
            {{ item.text }}
          </a>
        </div>
        <div *ngIf="item.children" class="accordion-item">
          <h2 class="accordion-header m-0" id="flush-{{ item.id }}">
            <div
              id="menu-button-{{ item.id }}"
              class="accordion-button collapsed cursor-pointer p-2"
              data-bs-toggle="collapse"
              attr.data-bs-target="#flush-collapse-{{ item.id }}"
              aria-expanded="false"
              attr.aria-controls="flush-collapse-{{ item.id }}"
            >
              <mat-icon class="ms-1 me-3">{{ item.icon }}</mat-icon>
              {{ item.text }}
            </div>
          </h2>
          <div
            id="flush-collapse-{{ item.id }}"
            class="accordion-collapse collapse"
            attr.aria-labelledby="flush-{{ item.id }}"
            data-bs-parent="#sidebarAccordion"
          >
            <ul class="list-unstyled mb-0">
              <li *ngFor="let child of item.children">
                <div
                  *ngIf="child.children"
                  class="accordion accordion-flush"
                  id="accordion-{{ child.id }}"
                >
                  <div class="accordion-item">
                    <h2 class="accordion-header m-0" id="flush-{{ child.id }}">
                      <div
                        id="menu-button-{{ child.id }}"
                        class="accordion-button collapsed cursor-pointer p-2"
                        data-bs-toggle="collapse"
                        attr.data-bs-target="#flush-collapse-{{ child.id }}"
                        aria-expanded="false"
                        attr.aria-controls="flush-collapse-{{ child.id }}"
                      >
                        <div class="ps-2 fw-normal d-flex align-items-center">
                          <mat-icon class="ms-1 me-3">{{
                            child.icon
                          }}</mat-icon>
                          {{ child.text }}
                        </div>
                      </div>
                    </h2>
                    <div
                      id="flush-collapse-{{ child.id }}"
                      class="accordion-collapse collapse"
                      attr.aria-labelledby="flush-{{ child.id }}"
                      attr.data-bs-parent="#accordion-{{ child.id }}"
                    >
                      <ul class="list-unstyled mb-0">
                        <li *ngFor="let subChild of child.children">
                          <a
                            class="
                              cursor-pointer
                              nav-item
                              ps-4
                              d-flex
                              align-items-center
                              non-dropdown
                            "
                            routerLink="{{ subChild.url }}"
                            routerLinkActive="active-route-3rd"
                          >
                            <mat-icon class="ms-1 me-3">{{
                              subChild.icon
                            }}</mat-icon>
                            {{ subChild.text }}
                          </a>
                        </li>
                      </ul>
                    </div>
                  </div>
                </div>
                <div *ngIf="!child.children">
                  <a
                    class="
                      cursor-pointer
                      nav-item
                      ps-3
                      d-flex
                      align-items-center
                      non-dropdown
                    "
                    routerLink="{{ child.url }}"
                    routerLinkActive="active-route-2nd"
                  >
                    <mat-icon class="ms-1 me-3">{{ child.icon }}</mat-icon>
                    {{ child.text }}
                  </a>
                </div>
              </li>
            </ul>
          </div>
        </div>
      </div>
    </div>
  </nav>

With the above code, I end up with the following (after styling obviously)

enter image description here

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Hedgybeats