CodeWarrior
CodeWarrior

Reputation: 5176

Make my accordion respond dynamically?

I have created an accordion component that fills the values dynamically provided by the parent component. What I am not able to achieve is that each accordion should respond to the respective click. Currently, no matter which accordion I click, it just collapses and expands the first one. Here is my code:

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>

What I really want to do is that each accordion should expand and collapse only when it is clicked. Currently, no matter which accordion I click only the first one expands and collapse. I know that it is because it gets a static ID. I tried a few possible options to assign a dynamic id to all the fields but haven't been successful. Any help is greatly appreciated.

Upvotes: 3

Views: 26527

Answers (3)

CodeWarrior
CodeWarrior

Reputation: 5176

Thanks everyone for the help. @Zze I built further on your idea to get exactly what I wanted. Here is my code that works the was I wanted. As in my case I had multiple headings and multiple accordion objects inside 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'
            }]
        }]
    }

Changed my html code to this

<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>

Upvotes: 1

Zze
Zze

Reputation: 18805

You need to bind the attributes that bootstrap relies on to toggle the div's accordingly:

Being:

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

Here is a snippet from the working example:

<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

Note that we have to prefix the aria attributes because they are not native attributes to <div> & <a>.

Upvotes: 10

Nour
Nour

Reputation: 5889

In your array component you expect an input of array type but actually you are passing an element not an array:

<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[]// You are expecting an array;

Try to pass your array:

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

Upvotes: 0

Related Questions