skip to Main Content

I am trying an implementation of accordion effect in one of my Angular views. The most basic accordion example you would find over at w3schools. Please take a look at this stackblitz url where I have tried to implement the same, using the same approach I used in my original project.

https://stackblitz.com/edit/stackblitz-starters-v54vdwmj

Nothing is working. What’s confusing is that there is no compilation error or runtime errors in the console which really doesn’t help at all. Had there been some sort of errors, it would have at least given me a pointer in some direction.

Can you get this accordion on stackblitz working?

Thanks!

2

Answers


  1. Head over to angular.dev and read the documentation, that is best to start your angular journey!

    First accordion is going to repeat so it should be a separate component.

    The content of the accordion, can be HTML, so we can use the angular concept content projection to project the content inside the accordion.

    We can listen for click event using (click) property binding and toggle a signal which tracks the open close state.

    The component will take an input signal, which is the title.

    <button class="accordion" (click)="toggle.set(!toggle())"  [ngClass]="{'active': toggle() }">{{title()}}</button>
    

    We can use property binding [title] to pass in the title.

    <app-accordion [title]="'Section 1'">
    

    Finally we use two directives ngClass and ngStyle to conditionally add the classes to the accordion.

    <button class="accordion" (click)="toggle.set(!toggle())"  [ngClass]="{'active': toggle() }">{{title()}}</button>
    <div class="panel" [ngStyle]="{'display': toggle() ? 'block' : 'none' }">
    

    Full Code:

    TS:

    import { Component, input, OnInit, signal } from '@angular/core';
    import { accordianUtility } from './utils/accordian-utility';
    import { NgClass, NgStyle } from '@angular/common';
    
    @Component({
      selector: 'app-accordion',
      imports: [NgClass, NgStyle],
      template: `
        <button class="accordion" (click)="toggle.set(!toggle())"  [ngClass]="{'active': toggle() }">{{title()}}</button>
        <div class="panel" [ngStyle]="{'display': toggle() ? 'block' : 'none' }">
          <p>
            <ng-content/>
          </p>
        </div>
      `,
    })
    export class Accordion implements OnInit {
      title = input('');
      toggle = signal(false);
      ngOnInit(): void {}
    }
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css'],
      imports: [Accordion],
    })
    export class AppComponent implements OnInit {
      ngOnInit(): void {}
    
      accordianClicked() {
        accordianUtility();
      }
    }
    

    HTML:

    <h2>Accordion</h2>
    
    <app-accordion [title]="'Section 1'">
      Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
      tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
      quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
      consequat.
    </app-accordion>
    <app-accordion [title]="'Section 2'">
      Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
      tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
      quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
      consequat.
    </app-accordion>
    <app-accordion [title]="'Section 3'">
      Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
      tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
      quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
      consequat.
    </app-accordion>
    

    Stackblitz Demo

    Login or Signup to reply.
  2. A javascript based accordion use generally a css class.

    You has define:

    .panel {
      padding: 0 18px;
      background-color: white;
      display: none;  /<--see that is always display none
      overflow: hidden;
    }
    

    You need add some like

    .active ~ .panel{  //<--when the button have the class "active"
      display:block    // the panel what is "Subsequent-sibling"
    }
    

    Well, another is that your function "accordianUtility" should be only execute one time. So use ngAfterViewInit in your component and remove the "accordianClicked".

    ngAfterViewInit(){
       acordianUtility()
    }
    

    your forked stackblitz

    More. You should avoid add listeners using javascript, why not use some more simple an "Angular event"?

    and your accordianClicked function in component reply the behaivour of the function in your "acordian-utily.ts

    <!--see how pass the html element using template reference variable (the "#bt")-->
    <button #bt (click)="accordianClicked(bt)" class="accordion">Summary</button>
    
    accordianClicked(bt:HTMLElement)
    {
          bt.classList.toggle('active');
          var panel = bt.nextElementSibling as HTMLElement;
          if (panel.style.maxHeight) {
            panel.style.maxHeight = '';
          } else {
            panel.style.maxHeight = panel.scrollHeight + 'px';
          }
    }
    

    NOTE: Really it’s better pass to the function the "panel" as argument

    NOTE2: Really I agree with Naren and JSON Derulo, your should use an "angular aproach". in ng-bootstrap you have a library with accordion, or you can use Bootstrap 5

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search