Angular中FormArray表格排序失效及MatTable改造咨询
问题原因及解决方案
为什么MatSort失效?
MatSort是Angular Material专为MatTable设计的配套指令,你用普通表格+FormArray循环渲染的方式,既没把表格和MatSort指令绑定,也没让数据源和MatSort建立关联逻辑,自然触发不了排序功能。MatSort的排序逻辑依赖MatTable的数据源(比如MatTableDataSource)监听排序事件、重新计算数据顺序并更新视图,普通表格不具备这些机制。
是否需要改用MatTable?
是的,想要复用MatSort的内置排序能力,必须切换到MatTable组件。它能和MatSort、MatPaginator等组件无缝整合,同时完全支持带输入框的编辑场景,不用放弃增删改功能。
改造实现带输入框的可排序MatTable
步骤1:组件类配置
在组件中准备FormArray和MatTableDataSource,同时获取MatSort实例:
import { Component, ViewChild, OnInit } from '@angular/core'; import { FormBuilder, FormArray } from '@angular/forms'; import { MatTableDataSource } from '@angular/material/table'; import { MatSort } from '@angular/material/sort'; interface Book { Id: number; Title: string; Description: string; } @Component({ selector: 'app-book-table', templateUrl: './book-table.component.html', styleUrls: ['./book-table.component.css'] }) export class BookTableComponent implements OnInit { displayedColumns: string[] = ['id', 'title', 'description', 'actions']; bookFormArray: FormArray; dataSource: MatTableDataSource<Book>; @ViewChild(MatSort) sort!: MatSort; constructor(private fb: FormBuilder) { // 初始化示例数据 const initialBooks: Book[] = [ { Id: 1, Title: 'Book 1', Description: 'Desc 1' }, { Id: 2, Title: 'Book 2', Description: 'Desc 2' } ]; // 将初始数据转为FormArray this.bookFormArray = this.fb.array( initialBooks.map(book => this.fb.group({ Id: book.Id, Title: book.Title, Description: book.Description })) ); // 初始化数据源 this.dataSource = new MatTableDataSource(initialBooks); } ngOnInit(): void { // 关联MatSort和数据源 this.dataSource.sort = this.sort; // 监听FormArray变化,同步更新数据源(确保排序后编辑的数据能同步) this.bookFormArray.valueChanges.subscribe(values => { this.dataSource.data = values; }); } // 获取FormArray的控件 get bookControls() { return this.bookFormArray.controls as FormArray[]; } // 添加书籍 addBook() { const newBook: Book = { Id: Date.now(), Title: '', Description: '' }; this.bookFormArray.push(this.fb.group(newBook)); this.dataSource.data = [...this.dataSource.data, newBook]; } // 删除书籍 deleteBook(index: number) { this.bookFormArray.removeAt(index); this.dataSource.data = this.dataSource.data.filter((_, i) => i !== index); } }
步骤2:模板配置
在模板中使用MatTable、MatSort,并在列中添加输入框绑定FormControl:
<div class="container"> <button mat-raised-button color="primary" (click)="addBook()">添加书籍</button> <table mat-table [dataSource]="dataSource" matSort class="mt-4"> <!-- ID列 --> <ng-container matColumnDef="id"> <th mat-header-cell *matHeaderCellDef mat-sort-header>ID</th> <td mat-cell *matCellDef="let element; let i = index"> <input type="number" [formControl]="bookControls[i].get('Id')"> </td> </ng-container> <!-- 标题列 --> <ng-container matColumnDef="title"> <th mat-header-cell *matHeaderCellDef mat-sort-header>标题</th> <td mat-cell *matCellDef="let element; let i = index"> <input type="text" [formControl]="bookControls[i].get('Title')"> </td> </ng-container> <!-- 描述列 --> <ng-container matColumnDef="description"> <th mat-header-cell *matHeaderCellDef mat-sort-header>描述</th> <td mat-cell *matCellDef="let element; let i = index"> <input type="text" [formControl]="bookControls[i].get('Description')"> </td> </ng-container> <!-- 操作列 --> <ng-container matColumnDef="actions"> <th mat-header-cell *matHeaderCellDef>操作</th> <td mat-cell *matCellDef="let element; let i = index"> <button mat-icon-button color="warn" (click)="deleteBook(i)"> <mat-icon>delete</mat-icon> </button> </td> </ng-container> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> </table> </div>
关键注意点
- 确保在
AppModule中导入MatTableModule、MatSortModule、MatButtonModule、MatIconModule等必要模块; - 数据源
dataSource和FormArray要保持同步,避免排序后编辑的数据和视图不一致; - 用
mat-sort-header标记需要排序的表头,点击表头就能触发排序。
内容的提问来源于stack exchange,提问作者vissiang
相关产品推荐
相关产品推荐

