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

如何在Angular 5中创建可复用的Angular Material数据表组件

打造可复用的Angular Material数据表组件

我完全懂你的痛点——重复写Angular Material表格代码真的太繁琐了!下面我来教你一步步打造一个高度可复用的数据表组件,以后再也不用复制粘贴那些重复的模板和逻辑了,只需要传入你的数据和配置就能快速生成表格。

第一步:创建通用数据表组件

首先咱们生成一个新的Angular组件,比如叫data-table:

ng generate component data-table

第二步:定义配置接口

在组件的.ts文件里,先定义几个接口来规范传入的配置,这样代码更清晰也更易维护:

// data-table.component.ts
import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core';
import { MatSort } from '@angular/material/sort';
import { MatPaginator } from '@angular/material/paginator';
import { MatTableDataSource } from '@angular/material/table';

// 定义列配置接口
export interface TableColumn<T> {
  columnDef: string;
  header: string;
  isSortable?: boolean; // 是否支持排序
  cell: (element: T) => string; // 单元格内容渲染函数
}

// 定义动作按钮接口
export interface TableAction {
  label: string;
  action: string; // 用来标识动作类型,比如'edit'/'delete'
  routerLink?: (element: any) => string; // 如果是路由跳转,返回链接
}

// 定义表格整体配置接口
export interface TableConfig<T> {
  data: T[];
  columns: TableColumn<T>[];
  actions?: TableAction[]; // 可选的动作列
  pageSizeOptions?: number[]; // 分页选项
  defaultSort?: { active: string; direction: 'asc' | 'desc' }; // 默认排序
}

第三步:实现组件逻辑

接下来在组件类里处理输入的配置,初始化数据源、排序、分页:

@Component({
  selector: 'app-data-table',
  templateUrl: './data-table.component.html',
  styleUrls: ['./data-table.component.scss']
})
export class DataTableComponent<T> {
  @Input() set config(config: TableConfig<T>) {
    if (config) {
      this._config = config;
      this.dataSource = new MatTableDataSource(config.data);
      // 设置排序
      if (this.sort && config.defaultSort) {
        this.sort.active = config.defaultSort.active;
        this.sort.direction = config.defaultSort.direction;
        this.dataSource.sort = this.sort;
      }
      // 设置分页
      if (this.paginator) {
        this.dataSource.paginator = this.paginator;
      }
      // 整理要显示的列(包括动作列)
      this.displayedColumns = config.columns.map(col => col.columnDef);
      if (config.actions && config.actions.length > 0) {
        this.displayedColumns.push('actions');
      }
    }
  }
  get config(): TableConfig<T> {
    return this._config;
  }
  private _config: TableConfig<T>;

  dataSource: MatTableDataSource<T>;
  displayedColumns: string[] = [];

  @ViewChild(MatSort) sort: MatSort;
  @ViewChild(MatPaginator) paginator: MatPaginator;

  // 触发动作事件,父组件可以监听
  @Output() actionTriggered = new EventEmitter<{ action: string; element: T }>();

  // 处理动作按钮点击
  onAction(action: TableAction, element: T) {
    if (action.action) {
      this.actionTriggered.emit({ action: action.action, element });
    }
  }
}

第四步:编写组件模板

现在写动态渲染的模板,根据传入的配置生成列、表头和单元格:

<!-- data-table.component.html -->
<div class="example-container">
  <mat-table [dataSource]="dataSource" matSort>
    <!-- 动态渲染普通列 -->
    <ng-container *ngFor="let column of config.columns" [matColumnDef]="column.columnDef">
      <mat-header-cell *matHeaderCellDef mat-sort-header="{{column.isSortable ? column.columnDef : ''}}">
        {{column.header}}
      </mat-header-cell>
      <mat-cell *matCellDef="let element">
        {{column.cell(element)}}
      </mat-cell>
    </ng-container>

    <!-- 动作列(如果有配置) -->
    <ng-container *ngIf="config.actions?.length > 0" matColumnDef="actions">
      <mat-header-cell *matHeaderCellDef>Action</mat-header-cell>
      <mat-cell *matCellDef="let element">
        <a 
          *ngFor="let action of config.actions"
          [routerLink]="action.routerLink ? action.routerLink(element) : null"
          class="btn Action-Tab"
          (click)="action.routerLink || onAction(action, element)"
        >
          {{action.label}}
        </a>
      </mat-cell>
    </ng-container>

    <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
    <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
  </mat-table>

  <mat-paginator 
    *ngIf="config.pageSizeOptions"
    [pageSizeOptions]="config.pageSizeOptions"
    showFirstLastButtons
  ></mat-paginator>
</div>

第五步:在父组件中使用通用表格

现在回到你的位置管理组件,只需要传入配置就能生成表格了,再也不用写重复的表格代码!

// location.component.ts
import { Component } from '@angular/core';
import { TableColumn, TableAction, TableConfig } from '../data-table/data-table.component';
import { Location } from './location.model'; // 假设你有Location模型

@Component({
  selector: 'app-location',
  templateUrl: './location.component.html',
  styleUrls: ['./location.component.scss']
})
export class LocationComponent {
  locations: Location[] = []; // 你的位置数据

  // 定义表格配置
  tableConfig: TableConfig<Location> = {
    data: this.locations,
    columns: [
      {
        columnDef: 'locationName',
        header: 'Location Name',
        isSortable: true,
        cell: (location) => location.locationName
      },
      {
        columnDef: 'address',
        header: 'Address',
        isSortable: false,
        cell: (location) => location.address
      },
      {
        columnDef: 'city',
        header: 'City',
        isSortable: true,
        cell: (location) => location.city
      },
      {
        columnDef: 'country',
        header: 'Country',
        isSortable: true,
        cell: (location) => location.country
      },
      {
        columnDef: 'zipcode',
        header: 'ZipCode',
        isSortable: false,
        cell: (location) => location.zipcode
      },
      {
        columnDef: 'phone',
        header: 'Phone',
        isSortable: false,
        cell: (location) => location.phone
      },
      {
        columnDef: 'timezone',
        header: 'TimeZone',
        isSortable: false,
        cell: (location) => location.timezone
      }
    ],
    actions: [
      {
        label: 'Edit',
        action: 'edit',
        routerLink: (location) => `/admin/location/edit/${location.locationID}`
      },
      {
        label: 'Delete',
        action: 'delete'
      }
    ],
    pageSizeOptions: [10, 20, 50, 100],
    defaultSort: { active: 'locationName', direction: 'asc' }
  };

  // 监听动作事件
  onTableAction(event: { action: string; element: Location }) {
    switch (event.action) {
      case 'delete':
        this.deleteLocation(event.element, event.element.locationID);
        break;
      // 其他动作可以在这里处理
    }
  }

  deleteLocation(location: Location, id: number) {
    // 你的删除逻辑
  }
}

然后父组件的模板就超级简洁了:

<!-- location.component.html -->
<app-data-table 
  [config]="tableConfig"
  (actionTriggered)="onTableAction($event)"
></app-data-table>

额外优化小技巧

  • 可以添加过滤功能,在通用组件里加一个输入框,绑定dataSource.filter
  • 支持自定义单元格模板(比如如果需要渲染图标或复杂内容,可以用ng-content或模板引用)
  • 把样式也通用化,比如表格的间距、按钮样式可以放在通用组件的scss里

这样一来,你所有需要表格的地方都可以复用这个app-data-table组件,只需要传入不同的配置和数据就行,彻底告别重复代码!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:08:36