Accordion Component Tutorial

A comprehensive guide to building complex forms with the ERTPL-UI Accordion component, featuring real-world examples from inventory management systems.

📦 Installation

Install the Package

npm install @vui/accordion

Import in Your Component

import { Component } from '@angular/core';
import { VuiAccordion } from '@vui/accordion';
import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';

@Component({
  selector: 'app-my-component',
  standalone: true,
  imports: [VuiAccordion, ReactiveFormsModule],
  // ... rest of component
})

⚠️ Important: CSS Setup for Proper Styling

If the accordion's look and feel is not appearing correctly when used as an npm module, you need to import the component's CSS styles.

Method 1: Import CSS in your styles.css

/* Add to your src/styles.css file */
@import 'node_modules/@vui/accordion/accordion.tailwind.css';

Method 2: Copy Required CSS

  1. Open node_modules/@vui/accordion/accordion.tailwind.css
  2. Copy lines 1-166 (all CSS before the utilities section)
  3. Paste into your project's src/styles.css file
Visual Features You'll Get:
  • ✨ Rounded corners with proper border management
  • 🎯 Focus states with violet ring highlighting
  • 🌈 Header highlighting when expanded (light violet background)
  • 🎨 Smooth transitions for all state changes
  • 📱 Responsive design for mobile and desktop

🚀 Basic Usage

Simple Accordion Example

This is the basic accordion content. Click the header to expand/collapse.

<ertpl-accordion
  title="Getting Started"
  [initiallyExpanded]="false"
  variant="default">

  <ng-template #content>
    <p>This is the basic accordion content. Click the header to expand/collapse.</p>
  </ng-template>
</ertpl-accordion>

🔧 Real-World Example: Search Form

This example shows how to build a complex search form using the accordion component with working expand/collapse functionality.

Working Accordion Demo

<ertpl-accordion
  title="Search Filters"
  [initiallyExpanded]="false"
  variant="default"
  (accordionToggled)="onSearchToggled($event)">

  <!-- Summary Template -->
  <ng-template #summary>
    <div class="text-xs text-gray-500">
      {{ getSearchSummary() }}
    </div>
  </ng-template>

  <ng-template #content>
    <form [formGroup]="searchForm" (ngSubmit)="onSearchSubmit()" class="space-y-4">

      <!-- Header with Actions -->
      <div class="flex items-center justify-between mb-6">
        <h3 class="text-lg font-medium text-gray-900">Search Form</h3>
        <div class="flex items-center space-x-3">
          <button type="button" (click)="onClearSearch()" class="px-4 py-2 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200">
            Clear
          </button>
          <button type="submit" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">
            Search
          </button>
        </div>
      </div>

      <!-- Form Fields Grid -->
      <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
        <div>
          <label class="block text-sm font-medium text-gray-700 mb-1">Name</label>
          <input type="text" formControlName="name" placeholder="Search by name..."
                 class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
        </div>

        <div>
          <label class="block text-sm font-medium text-gray-700 mb-1">Category</label>
          <select formControlName="category" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
            <option value="">Select category</option>
            <option value="electronics">Electronics</option>
            <option value="clothing">Clothing</option>
            <option value="books">Books</option>
          </select>
        </div>

        <div>
          <label class="block text-sm font-medium text-gray-700 mb-1">Status</label>
          <select formControlName="status" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
            <option value="">Any status</option>
            <option value="active">Active</option>
            <option value="inactive">Inactive</option>
          </select>
        </div>

        <div>
          <label class="block text-sm font-medium text-gray-700 mb-1">Date Range</label>
          <input type="date" formControlName="date" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
        </div>
      </div>
    </form>
  </ng-template>
</ertpl-accordion>

Component TypeScript Code

@Component({
  selector: 'app-search-form',
  standalone: true,
  imports: [
    CommonModule,
    ReactiveFormsModule,
    VuiAccordion
  ],
  // template shown above
})
export class SearchFormComponent implements OnInit {
  @Output() searchApplied = new EventEmitter<any>();
  @Output() searchCleared = new EventEmitter<boolean>();
  @Output() searchToggled = new EventEmitter<boolean>();

  searchForm!: FormGroup;

  constructor(private fb: FormBuilder) {}

  ngOnInit(): void {
    this.initializeForm();
  }

  private initializeForm(): void {
    this.searchForm = this.fb.group({
      name: [''],
      category: [''],
      status: [''],
      date: ['']
    });
  }

  onSearchToggled(isExpanded: boolean): void {
    console.log('Accordion toggled:', isExpanded);
    this.searchToggled.emit(isExpanded);
  }

  onSearchSubmit(): void {
    const formValue = this.searchForm.value;
    console.log('Search submitted:', formValue);
    this.searchApplied.emit(formValue);
  }

  onClearSearch(): void {
    this.searchForm.reset();
    this.searchCleared.emit(true);
  }

  getSearchSummary(): string {
    const values = this.searchForm.value;
    const filters = Object.entries(values)
      .filter(([_, value]) => value && value !== '')
      .map(([key, value]) => `${key}: ${value}`);

    return filters.length > 0 ? filters.join(', ') : 'No filters applied';
  }
}

✨ Key Features Demonstrated

📝 Summary Template

Shows dynamic filter summary when collapsed

{{ getSearchSummary() }}

🎯 Event Handling

Track accordion state and form submissions

(accordionToggled)="onSearchToggled($event)"

🏗️ Complex Forms

Multiple form controls with validation

ReactiveFormsModule integration

📱 Responsive Design

Grid layout adapts to screen size

grid-cols-1 md:grid-cols-4

📖 API Reference

Component Properties

Inputs

Property Type Default Description
title string 'Accordion' Header title text
initiallyExpanded boolean false Whether accordion starts expanded
disabled boolean false Disable accordion interaction
showIcon boolean true Show/hide chevron icon
variant 'default' | 'bordered' | 'filled' 'default' Visual styling variant

Outputs

Event Type Description
accordionToggled EventEmitter<boolean> Emitted when accordion expands/collapses
expanded EventEmitter<void> Emitted when accordion expands
collapsed EventEmitter<void> Emitted when accordion collapses

💡 Best Practices

✅ Do

  • • Use summary templates for complex forms to show filter status
  • • Handle accordion toggle events for analytics or state management
  • • Use overflow-visible class for dropdowns inside accordions
  • • Implement clear/reset functionality for form accordions
  • • Use debouncing for API calls triggered by form changes

❌ Don't

  • • Put too many form fields in a single accordion (split into multiple)
  • • Forget to handle form validation and submission errors
  • • Use accordions for critical actions that should always be visible
  • • Nest accordions inside other accordions (use tabs instead)
  • • Make accordion headers too vague - be descriptive