You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何区分Angular Material中多个封装的Mat Accordion组件

解决方案

要区分不同的手风琴实例,核心是给每个实例分配唯一标识,并在状态变更时将标识与状态一起传递。以下是具体修改步骤:

1. 更新数据模型,添加唯一ID

在SidePanelAccordianData中新增id字段,用于标识每个手风琴实例:

export class SidePanelAccordianData {
  public id: string; // 新增唯一标识
  public title: string = '';
  public panelOpenState: boolean;
  public expanded: boolean;

  constructor(args) {
    this.id = args.id; // 初始化id
    this.title = args.title;
    this.panelOpenState = args.panelOpenState;
    this.expanded = args.expanded;
  }
}

2. 修改状态服务,传递带标识的状态

将服务中的BehaviorSubject改为传递包含id和state的对象,让订阅者能识别是哪个实例的状态变更:

import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';

// 定义状态变更的类型
export interface AccordionStateChange {
  id: string;
  state: boolean;
}

@Injectable({
  providedIn: 'root'
})
export class SidePanelAccordianService {
  private _openStateBehaviorSubject = new BehaviorSubject<AccordionStateChange>({id: '', state: false});
  public openState$ = this._openStateBehaviorSubject.asObservable();

  public setOpenState(change: AccordionStateChange) {
    this._openStateBehaviorSubject.next(change);
  }
}

3. 更新封装组件,传递实例ID

修改SidePanelAccordianComponent的状态变更方法,触发服务时带上当前实例的id,同时修复原代码中事件绑定的赋值错误:

import { Component, Input, OnInit } from '@angular/core';
import { SidePanelAccordianData } from './side-panel-accordian.model';
import { SidePanelAccordianService, AccordionStateChange } from './side-panel-accordian.service';

@Component({
  selector: 'app-side-panel-accordian',
  templateUrl: './side-panel-accordian.component.html',
  styleUrls: ['./side-panel-accordian.component.scss']
})
export class SidePanelAccordianComponent implements OnInit {
  @Input() data: SidePanelAccordianData;

  constructor(private accordianStateService: SidePanelAccordianService) { }

  ngOnInit(): void {
  }

  stateValue() {
    this.data.panelOpenState = !this.data.panelOpenState;
    // 传递包含id和状态的对象
    const stateChange: AccordionStateChange = {
      id: this.data.id,
      state: this.data.panelOpenState
    };
    this.accordianStateService.setOpenState(stateChange);
  }

  // 处理面板打开事件
  handlePanelOpened() {
    this.data.panelOpenState = true;
    const stateChange: AccordionStateChange = {
      id: this.data.id,
      state: true
    };
    this.accordianStateService.setOpenState(stateChange);
  }

  // 处理面板关闭事件
  handlePanelClosed() {
    this.data.panelOpenState = false;
    const stateChange: AccordionStateChange = {
      id: this.data.id,
      state: false
    };
    this.accordianStateService.setOpenState(stateChange);
  }
}

同步修改模板中的事件绑定:

<mat-accordion>
    <mat-expansion-panel (opened)="handlePanelOpened()" (closed)="handlePanelClosed()" [expanded]="data.expanded">
        <mat-expansion-panel-header (click)="stateValue()">
            <mat-panel-title>
                {{ data?.title }}
            </mat-panel-title>
        </mat-expansion-panel-header>
        <ng-content></ng-content>
    </mat-expansion-panel>
</mat-accordion>

4. 在父组件中使用唯一ID并区分实例

给每个手风琴实例分配唯一id,订阅状态变更时根据id处理对应的展开状态:

父组件HTML

<app-side-panel-accordian [data]="{id: 'first-driver', title:'First Driver', panelOpenState: false, expanded: firstDriverExpanded}">
    <div class="inner-row-data">
      <form-input [formStatus]="formStatus" [parentFormGroup]="setupForm" [data]="{
        formControlName: 'firstDriverName',
        name: 'firstDriverName',
      }" (onBlur)="setFirstDriverDetails('name')">
      </form-input>
    </div>
</app-side-panel-accordian>

<app-side-panel-accordian [data]="{id: 'second-driver', title:'Second Driver', panelOpenState: false, expanded: secondDriverExpanded}">
    <div class="inner-row-data">
      <form-input [formStatus]="formStatus" [parentFormGroup]="setupForm" [data]="{
        formControlName: 'secondDriverName',
        name: 'secondDriverName',
      }" (onBlur)="setSecondDriverDetails('name')">
      </form-input>
    </div>
</app-side-panel-accordian>

父组件TS

this.accordianStateService.openState$.subscribe(change =>{
  // 根据id区分不同实例,实现自定义开闭规则
  switch(change.id) {
    case 'first-driver':
      this.firstDriverExpanded = change.state;
      // 示例规则:打开当前面板时关闭另一个
      // if(change.state) this.secondDriverExpanded = false;
      break;
    case 'second-driver':
      this.secondDriverExpanded = change.state;
      // 示例规则:打开当前面板时关闭另一个
      // if(change.state) this.firstDriverExpanded = false;
      break;
  }
});

额外说明

  • 原代码中(opened)="data.panelOpenState === true"是错误的,这是比较操作而非赋值,会导致状态无法正确更新,已在修改中修复。
  • 可根据业务需求在父组件的订阅回调中添加任意开闭规则,比如限制同时只能打开一个面板。

内容的提问来源于stack exchange,提问作者RRB

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 14:53:15