How can one efficiently implement a recursive dynamic expanding navigation bar in Angular, and what is the best method to toggle its menu items effectively?

I am currently developing an Angular 8 app and utilizing a bootstrap 4 layout in my project. One of the features I am working on is a menu with nested items, inspired by the Dynamic Expanding Nav Drawer example.

The challenge I am encountering is that when I click on a parent menu item, it expands all the nested items, which is not the behavior I desire.

Below is the code snippet I have attempted:

<div class="row">
  <div *ngTemplateOutlet="recursiveMenu; context:{ $implicit: navItems }"></div>
  <ng-template #recursiveMenu let-menus>
    <ul class="w-100">
      <li *ngFor="let menu of menus;">
        <a class="align-self-stretch text-left" href="#" title="{{menu.displayName}}" (click)="onItemSelected(menu)">
          <i class="fa {{menu.iconName}}"></i> <span class="nav-header-primary p-2">{{menu.displayName}}</span> &nbsp;
          <span *ngIf="menu.children" class="fa fa-caret-down fa-2x pull-right" [@indicatorRotate]="expanded ? 'expanded': 'collapsed'">
          </span>
        </a>
        <ul *ngIf="menu.children && expanded" id="nav{{menu.id}}">
          <ng-container *ngTemplateOutlet="recursiveMenu; context:{ $implicit: menu.children }"></ng-container>
        </ul>
      </li>
    </ul>
  </ng-template>
</div>

If you could provide assistance, here's the code output at this stackblitz link. Please let me know where I may be going wrong.

Answer №1

Whenever a parent item is expanded, all sub menu items are also getting expanded due to the common flag being used to check if the menu is in an expanded state. Instead of using a single flag for all menu items, it would be more efficient to maintain a separate flag for each item, within a map structure. To achieve this, you can introduce an id property in your menu list:

navItems: INavItem[] = [
    {
      id: 1,
      displayName: 'Home',
      iconName: 'fa-home fa-lg p-2',
      route: 'home'
    },
    {
      displayName: 'Settings',
      id: 2,
      iconName: 'fa-cog',
      children: [
        {
          displayName: 'Navigation Management',
          iconName: 'fa-sliders',
          id: 3,
          children: [
            {
              displayName: 'Navigation & Form Mapping',
              id: 4,
              iconName: 'fa-handshake-o',
              route: 'navigation-form-mapping'
            }]
    }]
}]

You can now use the "expanded" property as a map with key-value pairs. Update your onItemSelected function as follows:

onItemSelected(item: INavItem) {
    if (item.children && item.children.length) {
      this.expanded[item.id] = !this.expanded[item.id];
    }
}

Modify your html code accordingly:

 <span *ngIf="menu.children" class="fa fa-caret-down fa-2x pull-right" [@indicatorRotate]="expanded[menu.id] ? 'expanded': 'collapsed'">
 </span>
 <ul *ngIf="menu.children && expanded[menu.id]" id="nav{{menu.id}}">
      <ng-container *ngTemplateOutlet="recursiveMenu; context:{ $implicit: menu.children }"></ng-container>
 </ul>

See a Working Demo here

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

transform svg into css pattern

I've been attempting to create a "circle-Egg" in CSS, but I'm encountering some difficulties. I have an SVG circle that I want to use on my website and insert an image inside it. However, using Pattern and Image to insert an image inside the SVG ...

How come my button is initiating automatically instead of manually?

Working on developing an API using Angular 2 with the Janus media server has brought up an issue regarding the start button. When running Janus, the button initiates automatically instead of manually. The following function was implemented for this purpos ...

Mobile devices with a horizontal scroll

Having trouble with side scrolling on mobile devices. I've set two @media queries for tablet and mobile. Visit dev.bdbshop.com @media only screen and (max-width: 767px) { .wrap { max-width: 300px; } Could the issue be related to this? Any suggestio ...

Solving the issue of localizing the Datetime picker

My date input seems to be saving incorrectly in the database. When I enter a date like 18/02/2020, it gets saved as 17/02/2020 22:00:00. Is this a time zone issue? How can I fix this problem and thank you. Also, if I want to save the date with UTC, how do ...

Select and activate a single input from multiple options in Angular

I have multiple input fields with corresponding buttons that enable the input when clicked. I would like the behavior of the buttons to work in such a way that when one button is clicked, only that specific input field is enabled while the others are disab ...

Dealing with content extending into the previous column within CSS columns

I used this code pen example to perfectly illustrate the concept. To demonstrate, I applied the following CSS code. Try hovering over the second card in the top row. .custom-hover:hover { transform: scale(1.5); } In essence, when the top element of a ...

How to Create a Compact JavaFX ComboBox

I'm attempting to create a more compact version of the ComboBox, but no matter what I try, the space between the text and arrow button remains consistent. When using the following CSS: .combo-box-base > *.arrow-button { -fx-padding: 0 0 0 0; ...

Unable to understand the reason behind why the button is not being displayed

I'm having trouble with my quiz app. I have a button to submit answers and move on to the next question, but whenever I try to place it within the div that contains the quiz, the button disappears. Can someone please help me figure out what's wro ...

Comparison of Ajax and submit: Why am I not receiving identical responses?

Utilizing Ajax for submission and all global variables ending in box. Note that the initialization code is functioning correctly. function begin() { if (confirm("Proceed at your own risk as all consequences are yours!!")) { var usernameNa ...

Navigating through Firebase after login

Encountering an issue with navigating to the register page using a firebase authentication promise. Instead of displaying only the register page, both the login page and register page are shown simultaneously: Login page with social buttons Register page ...

Why is it that when I store a DOM element reference in a JavaScript Array, I cannot reuse that stored reference to add an event listener

I have a little confusion and I hope someone can help me out. I am facing an issue with dynamically created buttons, where each button has a unique id. To keep track of these buttons in a well-organized manner, I store their references using a simple two-d ...

Trigger an event in Angular 2 and send it to the main application component

I need to trigger an event from a component that serves as the starting point of my application. This particular component manages a websocket connection and I must send a message, hence the need to trigger this event. The bootstrap component only contain ...

Tips for adding a class to the end of the DOM class

Greetings! I'm currently working with the code below: for ( let x: number = 0; x < this._vcr.element.nativeElement.querySelectorAll(".ui-steps-item").length; x++) { let className: any = this._vcr.element.nativeElement.querySelectorAll( ...

Is there a way to eliminate the table-hover effect on specific rows in the table?

Here is some HTML: <table class="table table-striped table-bordered table-condensed table-hover"> .... </tr> <tr ng-repeat-end ng-show="modelRow.activeRow==car.name" class="hidden-table"> <td colspan="6"> ...

utilizing a decimal point that is not in accordance with my cultural norms

I have encountered a bug where, despite setting the language of Windows 10 to Italian and adjusting the decimal separator in the control panel, our website is unable to display numbers with it. Is there a way to instruct devextreme to use the comma speci ...

Repairing div after upward scrolling and maintaining its fixation after refreshing the page

I have encountered two issues with this particular example: One problem is that the fixed_header_bottom should stay fixed beneath the fixed_header_top when scrolling up, causing the fixed_header_middle to gradually disappear as you scroll up, or vice v ...

Adjustable div heights for text within inline elements

How can I make two inline-table divs (content and sidebar) have the same height regardless of their content? I've experimented with various attributes, but nothing seems to be working. You can view my website here. ...

Having trouble with element.scrollTo not functioning properly on mobile devices?

I have been working on creating a vertical scrolling carousel, and so far everything seems to be functioning well. I can scroll horizontally using buttons and swipe gestures successfully on the simulator. However, when I view the website on a real mobile d ...

Discover the art of concurrently listening to both an event and a timer in JavaScript

Upon loading the page, a timer with an unpredictable duration initiates. I aim to activate certain actions when the countdown ends and the user presses a button. Note: The action will only commence if both conditions are met simultaneously. Note 2: To cl ...

Angular2 display user's activity status on every individual page

How can I implement visitor status tracking for every page in angular2? I have a jwt based authentication system in place that is working correctly, but now I need to ensure that the visitor's login status is checked on each route. Here is an example ...