如何区分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
相关产品推荐
相关产品推荐

