javascriptangulartypescriptparent-childcard

Toggling between card info


I have a child component which displays information when you click on its button.

information-data.component.html

<div class="col-12 col-sm-12 col-md-12 col-lg-12">
  <div class="col-sm-12 col-md-12 col-lg-12 p-2 info">
    <h6  (click)="questionClick()">{{headName}}
      <fa-icon [icon]="icons.faInfoCircle" class="fa-1x text-primary" transform="shrink-2"></fa-icon>
    </h6>

  </div>
  <ng-template #childTemplate>
  <div class="row" [hidden]="visibleStatus">
    <div class="col-12 bulb-section">
      <div class="row m-0">
        <div class="icon-bulb col">
      <p class="ml-4">
        {{infoData}}
      </p>
        </div>
      </div>
    </div>
  </div>
  </ng-template>
  <ng-container *ngIf="!displayInfo; then childTemplate"></ng-container>
</div>

information-data.component.ts

export class InformationDataComponent {

  @ViewChild('childTemplate', { static: true }) childTemplate: TemplateRef<any>;

  constructor() { }
  @Input() infoData: string;
  @Input() visibleStatus: boolean;
  @Input() headName: string;
  @Input() displayInfo?: boolean;

  questionClick () {
          this.visibleStatus = !this.visibleStatus;
}
}

Scenario - In my parent component, I have 3 bootstrap cards which have different data each. When I click on a card, I only want the information for the respective card to be displayed, hiding the other two in this scenario.

<div class="card">
                <div class="card-body">
                  <information-data [infoData]="salonInfo"
                                               [headName]="salonHead"
                                               [visibleStatus]="true"
                                               [displayInfo]="true"
                  ></information-data>
                </div>
              </div>
            </div>
            <div class="col-4 col-sm-4 col-md-4 col-lg-4">
              <div class="card">
                <div class="card-body">
                  <information-data [infoData]="hospitalInfo"
                                               [headName]="hospitalHead"
                                               [visibleStatus]="true"
                                               [displayInfo]="true"
                  ></information-data>
                </div>
              </div>
            </div>
            <div class="col-4 col-sm-4 col-md-4 col-lg-4">
              <div class="card">
                <div class="card-body">
                  <information-data [infoData]="travelInfo"
                                               [headName]="travelHead"
                                               [visibleStatus]="true"
                                               [displayInfo]="true"
                  ></information-data>
                </div>
              </div>
        <div class="row pl-5 pr-5">
           <ng-container *ngTemplateOutlet="childComponent.childTemplate"></ng-container>
        </div>
</div>

Other scenarios - I use this child component widely in other parts of the application as shown below, and it should not be changed.

            <retail-information-data [infoData]="funcInfo"
                              [headName]="funcHead"
                              [visibleStatus]="funcVisibility"
             ></retail-information-data>

How can I go about implementing this functionality without affecting other parts of the code?


Solution

  • We can use the EventEmitter and @Output to call the parent code, which will store the index of which element was clicked, then we can use a ViewChildren to get all the child components that are present, then use the set index, to access the template.

    I also use *ngFor as syntax to access the child component template!


    CHILD TS

    import { CommonModule } from '@angular/common';
    import {
      Component,
      Input,
      ViewChild,
      TemplateRef,
      Output,
      EventEmitter,
    } from '@angular/core';
    import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
    import { faInfoCircle } from '@fortawesome/free-solid-svg-icons';
    
    @Component({
      selector: 'information-data',
      standalone: true,
      imports: [CommonModule, FontAwesomeModule],
      templateUrl: './child.component.html',
      styleUrl: './child.component.css',
    })
    export class ChildComponent {
      @ViewChild('childTemplate', { static: true })
      childTemplate!: TemplateRef<any>;
      icons = {
        faInfoCircle,
      };
      constructor() {}
      @Input() infoData!: string;
      @Input() visibleStatus!: boolean;
      @Input() headName!: string;
      @Input() displayInfo?: boolean;
      @Output() clickEvent: EventEmitter<void> = new EventEmitter<void>();
    
      questionClick() {
        this.visibleStatus = !this.visibleStatus;
        this.clickEvent.emit();
      }
    }
    

    CHILD HTML

    <div class="col-12 col-sm-12 col-md-12 col-lg-12">
      <div class="col-sm-12 col-md-12 col-lg-12 p-2 info">
        <h6 (click)="questionClick()">
          {{ headName }}
          <fa-icon
            [icon]="icons.faInfoCircle"
            class="fa-1x text-primary"
            transform="shrink-2"
          ></fa-icon>
        </h6>
      </div>
      <ng-template #childTemplate>
        <div class="row" [hidden]="visibleStatus">
          <div class="col-12 bulb-section">
            <div class="row m-0">
              <div class="icon-bulb col">
                <p class="ml-4">
                  {{ infoData }}
                </p>
              </div>
            </div>
          </div>
        </div>
      </ng-template>
      <ng-container *ngIf="!displayInfo; then childTemplate"></ng-container>
    </div>
    

    MAIN.TS

    import { CommonModule } from '@angular/common';
    import { Component, QueryList, ViewChild, ViewChildren, ChangeDetectorRef, } from '@angular/core';
    import { bootstrapApplication } from '@angular/platform-browser';
    import 'zone.js';
    import { ChildComponent } from './app/child/child.component';
    
    @Component({
      selector: 'app-root',
      standalone: true,
      imports: [CommonModule, ChildComponent],
      template: `
                <div class="card">
                  <div class="card-body">
                    <information-data 
                      [infoData]="salonInfo"
                      [headName]="salonHead"
                      [visibleStatus]="true"
                      [displayInfo]="true"
                      (clickEvent)="index = 0;"
                    ></information-data>
                  </div>
                </div>
                <div class="col-4 col-sm-4 col-md-4 col-lg-4">
                  <div class="card">
                    <div class="card-body">
                      <information-data 
                        [infoData]="hospitalInfo"
                        [headName]="hospitalHead"
                        [visibleStatus]="true"
                        [displayInfo]="true"
                        (clickEvent)="index = 1;"
                      ></information-data>
                    </div>
                  </div>
                </div>
                <div class="col-4 col-sm-4 col-md-4 col-lg-4">
                  <div class="card">
                    <div class="card-body">
                      <information-data 
                        [infoData]="travelInfo"
                        [headName]="travelHead"
                        [visibleStatus]="true"
                        [displayInfo]="true"
                        (clickEvent)="index = 2;"
                      ></information-data>
                    </div>
                  </div>
                </div>
                <div class="row pl-5 pr-5" *ngIf="getChild() as child">
                  <ng-container *ngTemplateOutlet="child"></ng-container>
                </div>
      `,
    })
    export class App {
      @ViewChildren(ChildComponent) childComponents!: QueryList<ChildComponent>;
      name = 'Angular';
      travelHead = '12asdf3';
      travelInfo = '12asdf3';
      index = 0;
      hospitalInfo = '12asdfasdf3';
      hospitalHead = '12asdfasdf3';
      salonHead = '12zxcvxcvz3';
      salonInfo = '12zxcvxcvz3';
    
      constructor(private cdr: ChangeDetectorRef) {}
    
      ngAfterViewInit() {
        this.cdr.detectChanges();
      }
    
      getChild() {
        return this.childComponents?.get(this.index)?.childTemplate;
      }
    }
    
    bootstrapApplication(App);
    

    Stackblitz Demo