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

Angular 17中复用Angular Material表格:动态操作按钮与父组件通信

问题描述

我采用Angular独立组件模式,创建了一个基于Angular Material的子组件用于展示父组件传入的动态数据。目前存在两个问题:

  1. 操作列的图标及相关配置无法从父组件动态传入,当前为硬编码的静态图标
  2. 子组件中点击操作图标后,无法将对应行的记录传递到父组件进行后续处理

表格操作列截图

解决方案

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 01:35:59