Is there a way to have my accordion adjust automatically?

I have developed a dynamic accordion component that populates its values from the parent component. However, I am facing an issue where each accordion does not respond individually to clicks. Whenever I click on any accordion, only the first one expands and collapses. Below is the code snippet:

array-parent.component.ts

import { Component, AfterViewInit, OnInit } from '@angular/core';

@Component({
    selector: 'my-array-parent',
    templateUrl: './array-parent.component.html',
})
export class ArrayParentComponent implements OnInit {
    private content: Test[];

    ngOnInit(): void {
        this.content = [{
            heading: 'header1',
            testData: [{
                title: 'title1',
                content: 'content1'
            }, {
                title: 'title2',
                content: 'content2'
            }]
        }, {
            heading: 'header2',
            testData: [{
                title: 'title1',
                content: 'content1'
            }, {
                title: 'title2',
                content: 'content2'
            }]
        }]
    }
}

export class Test {
    heading: string;
    testData: TestItem[];
}

export class TestItem {
    title: string;
    content: string;
}

array-parent.component.html

<ng-container *ngFor="let item of content">
    <my-array [data]="item"></my-array>
</ng-container>

array.component.ts

import { Component, Input, OnInit, AfterViewInit } from '@angular/core';

@Component({
    selector: 'my-array',
    templateUrl: './array.component.html'
})

export class ArrayComponent implements OnInit {

    @Input() data: any;

    private contentObj: any;

    constructor() { }

    ngOnInit(): void {
        this.contentObj = this.data;
    }
}

array.component.html

<h2>{{contentObj.heading}}</h2>
<div class="columnOne" id="accordion" role="tablist" aria-multiselectable="true">
    <div *ngFor="let item of contentObj.testData;">
        <div role="tab" id="headingone">
            <h4>
                <a role="button" data-toggle="collapse" data-parent="#accordion" href="#collapseone" aria-expanded="true" aria-controls="collapseone">
                    {{item.title}}
                </a>
            </h4>
            <div id="collapseone" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="headingone">
                <div class="panel-body">
                    {{item.content}}
                </div>
            </div>
        </div>
    </div>
</div>

My goal is to make each accordion expand and collapse individually upon clicking. Currently, only the first accordion responds because of the static ID assigned. I've tried assigning dynamic IDs but without success. Any assistance in resolving this issue would be highly appreciated.

Answer №1

To make Bootstrap work properly with your div elements, you must bind the necessary attributes:

These include:

  • panel-heading: id, href, aria-controls
  • panel-collapse: id, aria-labeledby

Check out a snippet of a functional example below:

<div class="panel-heading" role="tab" [id]="'heading'+data.id">
    <h4 class="panel-title">
        <a role="button" data-toggle="collapse" data-parent="#accordion" [href]="'#collapse'+data.id" aria-expanded="true" [attr.aria-controls]="'collapse'+data.id">
            {{data.header}}
        </a>
    </h4>
</div>
<div [id]="'collapse'+data.id" class="panel-collapse collapse" role="tabpanel" [attr.aria-labelledby]="'heading'+data.id">
    <div class="panel-body">
        {{data.content}}
    </div>
</div>

https://stackblitz.com/edit/angular-osvv72

Remember to add prefixes to the aria attributes since they are not default attributes for <div> or <a> elements.

Answer №2

Appreciation to all for the assistance. @Zze, I expanded on your suggestion to achieve my desired outcome. Below is the code that perfectly fulfills my requirements, especially since I had multiple headings and accordion elements nested within them.

this.content = [{
            heading: 'header1ds kfgdskg',
            id: 1,
            testData: [{
                title: 'title1 ds;olfhsdjkl',
                content: 'content1 sdkjfhdskj'
            }, {
                title: 'title2 asdlkgkf',
                content: 'content2 dsaghfdsf'
            }]
        }, {
            heading: 'header2 sdfdsfds',
            id: 2,
            testData: [{
                title: 'title1 sdfdsfs',
                content: 'content1 sdygfsdgf'
            }, {
                title: 'title2 bsdfdudtfsd',
                content: 'content2 sdk;fgdsugkft'
            }]
        }]
    }

Modified my HTML structure as follows:

<h2>{{contentObj.heading}}</h2>
<div class="columnOne" [id]="'accordion' + contentObj.id" role="tablist" aria-multiselectable="true">
    <div *ngFor="let item of contentObj.testData; let i = index;">
        <div role="tab" [id]="'heading' + contentObj.id">
            <h4>
                <a role="button" data-toggle="collapse" [attr.data-parent]="'#accordion' + contentObj.id"
                   [href]="'#collapse' + contentObj.id + i" aria-expanded="true" [attr.aria-controls]="'collapse' + contentObj.id + i">
                    {{item.title}}
                </a>
            </h4>
            <div [id]="'collapse' + contentObj.id + i" class="panel-collapse collapse in" role="tabpanel" [attr.aria-labelledby]="'heading' + contentObj.id">
                <div class="panel-body">
                    {{item.content}}
                </div>
            </div>
        </div>
    </div>
</div>

Answer №3

When working with your array component, make sure to pass an actual array as input instead of just an element:

<ng-container *ngFor="let item of content">
          <my-array [data]="item" //Here the item is an object></my-array>
</ng-container>

//////////////////////////////////////////////

export class ArrayComponent implements OnInit {

@Input() data: any[]// Ensure you are passing an array;

Make sure to pass the array correctly like this:

<ng-container>
      <my-array [data]="content" //Here the item is an object></my-array>
</ng-container>

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

Creating mock objects with Jest

I am currently delving into the world of jest testing. Here is a snippet from an implementation class I'm working with: import { ExternalObject } from 'external-library'; export class MyClass { public createInstance(settings : ISettings) ...

Error code 1 in Ionic V5 Capacitor - cordova-plugin-media indicates a problem with media playback

Despite installing the plugin and ensuring all necessary permissions are set, I am still encountering error code 1 with the media plugin. I have also included <application android:requestLegacyExternalStorage="true" /> in <edit-config&g ...

Do you have any suggestions for optimizing an Angular 15 reactive form that gets filled with data from an API?

Creating an Angular 15 Reactive Form with FormGroup As I delve into the documentation to construct a form with 4 inputs that are populated with data retrieved via GET requests and then updated upon submission through PUT requests, I find that it is functi ...

A component in Angular is able to inherit properties without the need to duplicate the constructor

I have been searching for a way to enhance the DRYness of my code by creating a solid Component parent. However, inheriting a Component from another Component requires duplicating the constructor(<services>){}, which goes against the DRY principle. T ...

Importing configuration file in CRA Typescript with values for post-deployment modifications

I am currently working on a React app that utilizes Create React App and Typescript. My goal is to read in configuration values, such as API URLs. I have a config.json file containing this data, here's a sample snippet with placeholder information: { ...

Universal NestJS GraphQL Guard

I am currently working on implementing a global guard to ensure that all requests receiving MUTATIONS from graphql require the token in their headers by default. The code below successfully achieves this: graphql-context-get-token.guard.ts import { ...

Expanding Lists in Bootstrap 3: A Step-by-Step Guide

I have been experimenting with Bootstrap's Example accordion code, which can be accessed via this link: http://jsfiddle.net/qba2xgh6/18/ <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true"> <div class="panel ...

The binding element 'params' is assumed to have a type of 'any' by default

I encountered an issue The binding element 'params' implicitly has an 'any' type. Below is the code snippet in question: export default function Page({ params }) { const { slug } = params; return ( <> <h1>{s ...

Is there a way to add zeros at the beginning of ZIP codes that have only 3 or 4 digits using LODASH or Typescript?

Looking at the JSON data below, it includes information on USPS cities, states, counties, latitude, longitude, and zip codes. With over 349,000 lines of data, it's very extensive. ... { "zip_code": 988, "latitude": 18.39 ...

Unable to retrieve values from nested objects in component.html

Hey fellow tech enthusiasts, I'm currently dealing with a complex nested JSON object retrieved from an API. Here's a snippet of the object: profile : { title:"Mr", personalInfo:{ fullNames: "John Doe", id ...

While utilizing the imodel.js front-end for designing a custom geometric model, I ran into an issue while trying to display it

Utilizing imodel.js front-end, I was able to design a customized geometric model featuring elements like a collection box. However, when placing the model within the existing SpatialViewState in bim, it failed to display properly in the current view. Sub ...

How can we prevent Express static from serving the index.html file?

Encountering an issue when serving my angular 5 app from node express, where express static serves index.html for domain.com, but works correctly for domain.com/something. Can anyone help me figure out how to solve this? app.use(express.static(path.join(_ ...

When an object in Typescript is clearly a function, it throws a 'cannot invoke' error

Check out this TypeScript code snippet Take a look here type mutable<A,B> = { mutate: (x : A) => B } type maybeMutable<A,B> = { mutate? : (x : A) => B; } const myFunction = function<A,B>(config : A extends B ? maybeMutab ...

Utilizing props in styled-components: A beginner's guide

I am trying to pass a URL to a component so that I can use it as the background image of the component. Additionally, I need to check if the URL is empty. Component: <BannerImg/> CSS (styled): `const BannerImg = styled.img` background-image: url( ...

Is it possible to receive a notification when the DOM elements of an Angular 2+ component are successfully rendered in the browser?

I have a parent component in Angular that contains over 1000 DOM nodes. These nodes are rendered using two ngFor cycles, resulting in a 2D table structure. Each of these DOM nodes is an individual Angular component. The styles and displayed values of these ...

Insert a new row below an existing row within a Material table

Is it possible to dynamically insert a new row under a specific existing row in a table within the DOM without having to redefine all of the data in the MatTableDataSource? ...

Iterating through an array and setting variables according to asynchronous code

I have created a function to loop through an array, call a promise, and update a variable based on the result. The code seems to be functioning correctly, but I am wondering if there is a more optimal way to write it. Any suggestions are appreciated. Tha ...

Issues have been encountered with Angular 5 when trying to make required form fields work properly

Just created my first Angular app using Angular 5! Currently following the documentation at: https://angular.io/guide/form-validation. Below is the form I have set up: <form class="form-container"> <mat-form-field> <input matInput pl ...

Discovering the best approach to utilizing Font Awesome 6 with React

Required Packages "@fortawesome/fontawesome-svg-core": "^6.1.1", "@fortawesome/free-solid-svg-icons": "^6.1.1", "@fortawesome/react-fontawesome": "^0.1.18", "next": " ...

Acquiring a second access token in Java for the Graph API using an OIDC-compliant token can be achieved through the OBO flow method

Utilizing the angular-oauth2-oidc library within Angular allows me to login through the PKCE Authorization Flow, followed by passing the token to my backend in order to secure my custom API. The Spring boot backend functions as the oauth2 Resource Server ...