Custom Angular directive for collapsing sub menus with CSS

I found a helpful article on creating collapsible menus and submenus using only Bootstrap and custom code for Angular 2, 4, 5, 6. I've been able to implement the logic successfully, but I'm facing an issue with multiple menus where clicking on another menu does not collapse the one that is already open. Additionally, when clicking on a submenu item, the main menu collapses instead of routing properly.

Here's a demo on StackBlitz: https://stackblitz.com/edit/angular-y3ud5q

submenu.component.html

<aside class="main-sidebar">
    <section class="sidebar">
        <ul class="sidebar-menu tree" data-widget="tree">
            <li checkToggle *ngFor="let data of listsvalue ">
                <a>
                    <span>{{data.value}}</span>
                    <span class="pull-right-container">
                        <i class="fa fa-angle-left pull-right"></i>
                    </span>
                </a>
                <ul class="nav submenu" data-widget="tree">
                    <li *ngFor="let test of data.value ">
                        <a>
                            <span>{{test.value}}</span>
                            <span class="pull-right-container">
                                <i class="fa fa-angle-left pull-right"></i>
                            </span>
                        </a>
                    </li>
                </ul>
            </li>
        </ul>
    </section>
</aside>

submenu.directive.ts

@Directive({
  selector: '[checkToggle]'
})
export class SidebarLeftToggleDirective {
  @Input('checkToggle') partner;

  /**
  * @method constructor
  * @param elementRef [description]
  */
  constructor(public elementRef: ElementRef) { }

  @HostBinding("class.active") isOpen = false;
  @HostListener("click") toggleOpen($event) {
    this.isOpen = !this.isOpen;
  }
}

submenu.component.cs

.submenu li {
    padding-left:15px;
}

ul li .submenu {
    display: none;
}

ul li.active .submenu {
    display: block;
    list-style: none;
}

Answer №1

Using an inline statement and the class attribute is a simpler solution compared to Hostbinding in the directive. It allows for tracking just one line of code.

All you have to do is track a property that will store the current opened list item index, and the class "active" will only be assigned under that condition:

<li [class.active]="opened==i" *ngFor="let innerData of partner.value | keyvalue; let i=index" (click)="opened=opened==i?-1:i">

With this approach, every time there is a click event, the property evaluation will toggle between classes, either opening or collapsing the list items.

Check out the 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

How can I change the displayed value of a 'select' element programmatically in Internet Explorer?

My interactive graphic has a drop-down menu implemented with a <select> element. Everything functions correctly, except when using Internet Explorer. The issue arises when attempting to programmatically change the selected value of the <select> ...

Tips for automatically scrolling to the bottom of an element in NgFor when adding new elements

import { Component, Input, AfterViewInit, ViewChild } from '@angular/core'; @Component({ selector: 'hello', template: `<h1>Hello {{name}}!</h1> <div #commentDetailWrapper style="height: 100px; border: 1px solid; w ...

The identification of the field is not being transmitted by ng-select

Looking for help with Angular! I have an ng-select box where I can choose countries, and it's working fine when I select an option and submit - it submits the id as expected. However, when pre-populating the ng-select with data and submitting, it&apos ...

Building a Table Using HTML and CSS

I'm currently tackling this school assignment and have hit a roadblock with two issues. The HTML Validation error I'm encountering is regarding a table row that is 5 columns wide, exceeding the established count by the first row (4). From line 7 ...

Django works perfectly on local host, but unfortunately, hosting servers are not cooperating

Programming is a new skill for me and I recently created a weather app using Django. The app functions perfectly when I run it locally with the server, but I've encountered difficulties when trying to host it on various platforms. Here is the link to ...

Exploring Angular testing by using mock services to simulate the behavior of other services

I am familiar with how to mock a function call to a service. However, I am facing a scenario where my MainService acts as a wrapper for multiple other services. export class MainService { constructor( public service1: Service1, public service2 ...

Is there a way to display the data from a URL as selectable options in a dropdown menu?

I have a URL containing an arrayList of data. My task is to fetch the data from this URL and display it as options in a dropdown menu, allowing users to select the desired option. I am aware that this can be achieved using the Get method, but I am facing d ...

Modifying the delimiter used in paste range feature of ag-grid

Is there a way to enable pasting tab separated data into an ag-grid column instead of a row? Currently, when pasting newline separated data it goes into columns and tab separated goes into rows. The documentation suggests using the "clipboardDeliminator" ...

Retrieve all hyperlinks from HTML using C programming

Is there a way to extract all URLs from an HTML document using the C standard library? I attempted to use sscanf() for this purpose, but encountered errors when checking with valgrind. I'm unsure if my code will meet the requirements even after succe ...

What is the process for refreshing the dropdown menu in angular2 after modifying the data in the typescript file?

Right now, I am implementing angular2 in my project. I have a dropdown component labeled as CFC Filter {{val}} In the typescript file, I have defined this.filters = ["0", "60", "100", "180", "600", "1000"]; If the filter value retrieved from the da ...

How to capture a screenshot of the current screen using Nativescript programmatically

After taking a screenshot in a NativeScript app, is there a way to display a popup asking if the user wants to save the picture? I attempted using the 'nativescript-screenshot' plugin, but it only copies elements within the application: nat ...

Sending you to a new page and popping up an alert

I have set up an HTML form for user registration and after a successful registration, I want users to be redirected back to my index.html page with an alert confirming their successful registration. Currently, the alert is working but it opens in a blank ...

Is it feasible to make references to interfaces from an extended interface in Typescript?

I had the idea of enhancing all interfaces in HTMLElementTagNameMap with chained functionality. Since there are numerous interfaces, and all elements either are HTMLElement or extend it, I wanted a way to achieve something like this: interface HTMLElement ...

Setting up Stylelint in a Next.js project: A step-by-step guide

I'm looking to incorporate Stylelint into my Next.js app. Can I modify the next.config.js file to include the stylelint-webpack-plugin, in order to receive style errors during compilation? // next.config.js module.exports = { reactStrictMode: true, ...

Step-by-step guide for installing the source code of Bootstrap v3.3.5

Previously, I was utilizing version 3.3.4 of Bootstrap with a source code package that worked perfectly fine. Now, I am attempting to upgrade to version 3.3.5 but encountering issues. Here are the steps I have followed: I downloaded the zip folder for v3. ...

Error: The body is not usable - POST action from NextJS server

In my project with NextJS v14.1.0, I encountered an issue while using server action in a client component. The error message is showing correctly, but I also receive a TypeError stating that the body is unusable. src/app/auth/account-verification/page.tsx ...

preserving the current state even after refreshing the page

Currently, I am teaching myself React. When I click on the favorites button, which is represented by a heart symbol, it changes color. However, upon refreshing the page, the color change disappears. To address this issue, I came across the following helpfu ...

Click to enlarge font size across the entire project

I'm working on a project for elderly users and my client wants a feature where they can easily adjust the font size throughout the application with just one click of a button. What is the best approach for implementing this functionality? My initial ...

Adjusting the height of a textarea within a table

My textarea's height is supposed to be 500%, but it's not changing. I suspect that being inside a table might have something to do with the issue, but I'm unsure of what needs to be adjusted to set the height correctly. Surprisingly, the wid ...

Encountering a situation where the data retrieved from Firestore in Angular TypeScript is returning as

I am encountering an issue with retrieving the eventID value from my Events collection in Firestore. Although I am able to successfully display all the events in my app, I require the eventID for additional functionalities. As someone new to TypeScript an ...