FakeEmpire
FakeEmpire

Reputation: 798

How to apply a class to just one individual component after ngFor

I am creating an accordion component that expands and collapses when clicking a 'plus/minus' svg. This expanding and collapsing should be toggled and only effect one accordion component at a time, independent of the others. Right now, the accordions expand and collapse together. How do I target the class of each individual component?

app.component.html

<app-accordion>
</app-accordion>

accordion.component.html

<div
*ngFor="let person of people"
class="accordion noHighlight"
[ngClass]="currentClass">
  <app-accordion-header
  (handleExpansion)="handleExpansion($event)"  
  [person]="person">
  </app-accordion-header>
</div>

accordion.component.ts

import { AccordionService } from "./../accordion.service";
import { Component, Input, OnInit } from "@angular/core";

@Component({
  selector: "app-accordion",
  templateUrl: "./accordion.component.html",
  styleUrls: ["./accordion.component.css"],
  providers: [AccordionService]
})
export class AccordionComponent implements OnInit {
  plus: string = "../../assets/images/plus.svg";
  minus: string = "../../assets/images/minus.svg";
  people;
  currentClass;
  isOpen;


  constructor(private accordionService: AccordionService) {}

  ngOnInit() {
    this.accordionService.getPeople().subscribe(people => {
      this.people = people;
    });
  }

  toggleOpen(open) {
    open ? (this.currentClass = "expand") : (this.currentClass = "collapse");
  }

  handleExpansion(val) {
    this.isOpen = val;
    this.toggleOpen(this.isOpen);
  }

accordion-header.component.html

<div> 
  <div>
    <div>
        <app-plus (handleToggle)="handleToggle($event)"></app-plus>
    </div>
    <div>
      <span>{{person.name}}</span>
    </div>
    <div>
      <span>{{person.age}}</span>
    </div>
    <div>
      <span>{{person.description}}</span>
    </div>
  </div>    
</div> 

accordion-header.component.ts

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

@Component({
  selector: "app-accordion-header",
  templateUrl: "./accordion-header.component.html",
  styleUrls: ["./accordion-header.component.css"]
})
export class AccordionHeaderComponent implements OnInit {

  @Input() person;
  @Output() handleExpansion = new EventEmitter<boolean>()
  isOpen;

  constructor() {}

  ngOnInit() {}

  handleToggle(e){
    this.handleExpansion.emit(e)
  }

}

plus.component.ts

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

@Component({
  selector: "app-plus",
  templateUrl: "./plus.component.html",
  styleUrls: ["./plus.component.css"]
})
export class PlusComponent {
  @Output() handleToggle = new EventEmitter<boolean>();
  isOpen: boolean = false;
  icon = "";

  toggleIcon(toggleOpen) {
    toggleOpen ? this.icon = "open" : this.icon = "closed";
  }

  toggleExpansion() {
    this.isOpen = !this.isOpen;
    this.handleToggle.emit(this.isOpen);

    this.toggleIcon(this.isOpen);
  }
}

plus.component.html

<div (click)="toggleExpansion()">
  <svg>
      <g fill="#000000">
        <rect 
        id="plusicon-vertbar"
        class="plusicon-vertbar"
        [ngClass]="icon"
        x="8" 
        y="0" 
        width="4"
        height="20">
        </rect>
        <rect 
        id="plusicon-horizbar"
        transform="translate(10.000000, 10.000000) rotate(90.000000) translate(-10.000000, -10.000000)"
        x="8" 
        y="0" 
        width="4"
        height="20">
        </rect>
      </g>
    </g>
  </svg>
</div>

Upvotes: 0

Views: 146

Answers (1)

David Ibl
David Ibl

Reputation: 911

You are having one component with one class controlling the open state of the accordion segments. But within the template there are several accordion segments bound to the same class variable. To achieve what you want, you have to create a component for every accordion segment defining it's very own state variable. Like the PlusComponent. The easiest way is to embedd header body and button into one expandable component. After all ther should be something like:

<accordion-segment *ngFor="let person of people" [person]="person"></accordion-segment>

in the template.

This new component could look like this:

template:

<div class="accordion noHighlight" [ngClass]="currentClass">
    <app-accordion-header
         (handleExpansion)="handleExpansion($event)">
    </app-accordion-header>
</div>

Component:

@Component({selektor: 'accordion-segment', templateUrl: accordion-segment.html})
export class AccordionSegmentComponent {
    public currentClass = false;
    @Input()
    public person: any;
    public handleExpansion(event) {
        this.currentClass = !this.currentClass;
    }
}

You just have to handle closing others when a segment is opened. But the variable controlling the open state of one segment is within one unique segment component.

Upvotes: 1

Related Questions