Nooh Ahamad
Nooh Ahamad

Reputation: 393

How to make an Accordion/Zippy in Angular?

I am currently working on a project where I display a list of complaints in a zippy/accordion. I receive an array of key-value pairs as my dataset in my component.ts file, given below as:

isExpanded: boolean;

complaints: any[] = [
  {"CALLER”:"1234567890", "DURATION”:"45", "CALLTIME":"2020-06-14T02:12:00.000Z", "COMPLAINTTITLE":"Device Not Working", "COMPLAINTMESSAGE":"My device has some issue"},
  {"CALLER":"1357924680", "DURATION":"83", "CALLTIME":"2020-06-14T06:23:58.000Z", "COMPLAINTTITLE":"Device Not Working", "COMPLAINTMESSAGE":"My device has some issue"},
  {"CALLER":"2468013579", "DURATION":"123", "CALLTIME":"2020-06-14T10:44:20.000Z", "COMPLAINTTITLE":"Device Not Working", "COMPLAINTMESSAGE":"My device has some issue"},
  {"CALLER":"0864297531", "DURATION":"94", "CALLTIME":"2020-06-14T15:21:04.000Z", "COMPLAINTTITLE":"Device Not Working", "COMPLAINTMESSAGE":"My device has some issue”},
  {"CALLER”:"9753108642", "DURATION":"126", "CALLTIME":"2020-06-14T20:18:50.000Z", "COMPLAINTTITLE":"Device Not Working", "COMPLAINTMESSAGE":"My device has some issue”}
];

toggle(){
  this.isExpanded = !this.isExpanded;
}

In my component.html file, I have used an ngFor directive to populate the zippy.

<div class="zippy" *ngFor="let complaint of complaints”>
  <div class="zippy-heading" [class.expanded]="isExpanded" (click)="toggle()">
    <p>
      Caller: {{ complaint.CALLER }}<br>
      Time: {{ complaint.CALLTIME }}<br>
      Duration: {{ complaint.DURATION }}&nbsp;sec<br>
      Title: {{ complaint.COMPLAINTTITLE}}
    </p>
  </div>
  <div *ngIf="isExpanded" class="zippy-body">
    <p>Message: {{ complaint.COMPLAINTMESSAGE }}</p>              
  </div>
</div>

I have noticed that when I click on any title div, all of the messages are displayed. How do I get to open only the selected one that I had clicked?

Upvotes: 0

Views: 343

Answers (1)

Alexander Staroselsky
Alexander Staroselsky

Reputation: 38847

At minimum you could move div.zippy and it's children to a new component where each has it's own isExpanded. That way when one is expanded, it does not effect others:

Parent (loop over complaints and pass complaint as input):

<div class="whatever">
    <app-foo *ngFor="let complaint of complaints" [complaint]="complaint"></app-foo>
</div>

Child Template:

<div class="zippy">
  <div class="zippy-heading" [class.expanded]="isExpanded" (click)="toggle()">
    <p>
      Caller: {{ complaint.CALLER }}<br>
      Time: {{ complaint.CALLTIME }}<br>
      Duration: {{ complaint.DURATION }}&nbsp;sec<br>
      Title: {{ complaint.COMPLAINTTITLE}}
    </p>
  </div>
  <div *ngIf="isExpanded" class="zippy-body">
    <p>Message: {{ complaint.COMPLAINTMESSAGE }}</p>              
  </div>
</div>

Child Component:

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

@Component({
  selector: "app-foo",
  templateUrl: "./foo.component.html",
  styleUrls: ["./foo.component.css"]
})
export class FooComponent {
  @Input() complaint: any; // ideally use strict typing instead

  isExpanded: boolean = false;

  toggle() {
    this.isExpanded = !this.isExpanded;
  }
}

Here is an example in action.

The next steps would be to use a strategy from component interaction to perhaps close other elements/complaints when another is toggled. Keep in mind you can also add a open/close state value to the complaints to track/manipulate that state.

Hopefully that helps!

Upvotes: 1

Related Questions