Angular 17中复用Angular Material表格:动态操作按钮与父组件通信
问题描述
我采用Angular独立组件模式,创建了一个基于Angular Material的子组件用于展示父组件传入的动态数据。目前存在两个问题:
- 操作列的图标及相关配置无法从父组件动态传入,当前为硬编码的静态图标
- 子组件中点击操作图标后,无法将对应行的记录传递到父组件进行后续处理

解决方案
1. 动态渲染操作按钮
修正子组件操作列的渲染逻辑,从父组件传入的columns配置中读取操作按钮信息,实现动态渲染:
- 从
columns数组中筛选出name为actions的列,获取其buttons配置数组 - 循环遍历
buttons数组,动态绑定每个按钮的icon、class等属性
2. 传递点击事件到父组件
利用Angular的@Output输出事件机制,将子组件的点击事件及行数据传递给父组件:
- 子组件定义
@Output事件,触发时携带当前行数据和按钮类型 - 父组件在模板中绑定该事件,根据按钮类型分发到对应的业务处理方法
修改后的父组件代码
TypeScript逻辑
ngOnInit() { this.columns = [ { title: "Country", name: "countryName" }, { title: "State", name: "state" }, { title: "City", name: "cityName" }, { title: "Created By", name: "createdBy" }, { title: "Created Date", name: "createdAt" }, { title: "Action", name: 'actions', buttons: [ { type: "edit", icon: "edit", class: "tbl-fav-edit", title: ActionButtonType.Edit }, { type: "delete", icon: "trash-2", class: "tbl-fav-delete", title: ActionButtonType.Delete } ] } ] this.getAllCity(); } btnEditClick(row: any) { // 自定义编辑逻辑,如打开编辑弹窗、跳转编辑页等 console.log('编辑行数据:', row); } btnDeleteClick(row: any) { // 自定义删除逻辑,如弹出确认框、调用删除接口等 console.log('删除行数据:', row); } handleButtonClick(event: {row: any, buttonType: string}) { const { row, buttonType } = event; if (buttonType === 'edit') { this.btnEditClick(row); } else if (buttonType === 'delete') { this.btnDeleteClick(row); } }
模板代码
@if (flg) { <app-dynamic-mat-table [workList]="workList" [columns]="columns" (buttonClick)="handleButtonClick($event)" ></app-dynamic-mat-table> }
修改后的子组件代码
TypeScript逻辑
import { Component, Input, OnInit, ViewChild, Output, EventEmitter } from '@angular/core'; import { MatTableDataSource, MatTableModule } from '@angular/material/table'; import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator'; import { MatIconModule } from '@angular/material/icon'; import { DatePipe } from '@angular/common'; import { FeatherIconsComponent } from '../feather-icons/feather-icons.component'; @Component({ selector: 'app-dynamic-mat-table', standalone: true, imports: [ MatTableModule, MatPaginatorModule, MatIconModule, DatePipe, FeatherIconsComponent ], templateUrl: './dynamic-mat-table.component.html', styleUrl: './dynamic-mat-table.component.scss' }) export class DynamicMatTableComponent implements OnInit{ @ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator; @Input() workList: any[] = []; @Input() columns: any[] = []; @Output() buttonClick = new EventEmitter<{row: any, buttonType: string}>(); dataSource: MatTableDataSource<any>; displayedColumns: string[] = []; constructor(){ this.dataSource = new MatTableDataSource(); } ngOnInit(): void { this.dataSource.data = this.workList; this.displayedColumns = this.columns.map(column => column.name); this.dataSource.paginator = this.paginator; } getColumnTitle(columnName: string): string { const column = this.columns.find(col => col.name === columnName); return column ? column.title : ''; } getActionButtons(): any[] { const actionColumn = this.columns.find(col => col.name === 'actions'); return actionColumn?.buttons || []; } onButtonClick(row: any, buttonType: string) { this.buttonClick.emit({row, buttonType}); } }
HTML模板代码
<div class="responsive_table"> <table mat-table [dataSource]="dataSource" matSort class="mat-cell advance-table"> @for (column of displayedColumns; track column) { <ng-container [matColumnDef]="column"> <th mat-header-cell *matHeaderCellDef mat-sort-header> {{ getColumnTitle(column) }} </th> <td mat-cell *matCellDef="let element"> @if(column === 'createdAt'){ {{ element[column] | date: 'dd-MM-yyyy' }} }@else if(column !== 'actions'){ {{ element[column] }} }@else { @for(button of getActionButtons(); track button.type){ <span class="action-btn" (click)="onButtonClick(element, button.type)"> <app-feather-icons [icon]="button.icon" [class]="button.class" [title]="button.title" ></app-feather-icons> </span> } } </td> </ng-container> } <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> </table> <mat-paginator [pageSizeOptions]="[5, 10, 20]" showFirstLastButtons></mat-paginator> </div>
内容的提问来源于stack exchange,提问作者Abhinay kumar Mishra
相关产品推荐
相关产品推荐

