如何在Angular 15中实现HTML表格单元格的横竖合并?
Angular 15动态表格实现多方向相邻单元格合并
需求说明
需要在Angular 15的动态HTML表格中,实现所有值相同的相邻单元格合并,支持垂直(上下)和水平(左右)两个方向,不受列或行的限制。例如:
string string string string string string => string string string string

现有基础代码
HTML结构
<!-- HTML File --> <div class="table-wrapper"> <table class="fl-table" aria-describedby="table-description"> <tbody> <tr *ngFor="let row of tableData"> <ng-container *ngFor="let columnName of columnNames"> <td>{{ row[columnName] }}</td> </ng-container> </tr> </tbody> </table> </div>
基础TS代码
columnNames: any[] = []; tableData: any[] = []; getColumnNames(data: any) { if (data.length > 0) { this.columnNames = Object.keys(data[0]); } }
尝试过的方案(未达预期)
当前尝试的mergeCells方法仅实现了部分垂直合并,效果不符合预期:
尝试的TS代码
ngAfterViewInit(): void { this.mergeCells(); } mergeCells(): void { const tableElement: HTMLTableElement = this.elementRef.nativeElement.querySelector('table'); let headerCells: HTMLTableCellElement[] = []; for (let rowIndex = 0; rowIndex < tableElement.rows.length; rowIndex++) { const row = tableElement.rows[rowIndex]; for (let colIndex = 0; colIndex < row.cells.length; colIndex++) { const cell = row.cells[colIndex]; if (cell.innerText.trim() === '') { continue; } if (headerCells[colIndex] === undefined || cell.innerText !== headerCells[colIndex].innerText) { headerCells[colIndex] = cell; } else { headerCells[colIndex].rowSpan++; cell.remove(); } } } }
参考表格数据
tableData: any[] = [ { "0": "", "1": "", "2": "key1", "3": "key1", "4": "key2", "5": "key2", "6": "value5", "7": "value5", }, { "0": "", "1": "", "2": "key1", "3": "key1", "4": "key2", "5": "key2", "6": "value5", "7": "value5", }, { "0": "", "1": "", "2": "Count", "3": "Amount", "4": "Count", "5": "Amount", "6": "Count", "7": "Amount", }, { "0": "Loaded Data", "1": "All", "2": 245205, "3": "495693992.68", "4": 238112, "5": "475560898.63", "6": 247642, "7": "53858508052.00", }, { "0": "Status", "1": "value1", "2": 233398, "3": "464734379.76", "4": 233398, "5": "464734379.76", "6": 233398, "7": "46473437976.00", }, { "0": "Status", "1": "value2", "2": 11807, "3": "30959612.92", "4": 4714, "5": "10826518.87", "6": 14244, "7": "7385070076.00", }, { "0": "key2", "1": "value1", "2": 237591, "3": "473886210.33", "4": "", "5": "", "6": 233900, "7": "46634667766.00", }, { "0": "key2", "1": "value2", "2": 7614, "3": "21807782.35", "4": "", "5": "", "6": 13742, "7": "7223840286.00", }, { "0": "key2", "1": "value3", "2": 7084, "3": "20074807.55", "4": "", "5": "", "6": 13584, "7": "7173509166.00", }, { "0": "key2", "1": "value4", "2": 530, "3": "1732974.80", "4": "", "5": "", "6": 158, "7": "50331120.00", }, { "0": "value5", "1": "value1", "2": 233398, "3": "464734379.76", "4": 233900, "5": "466346677.66", "6": "", "7": "", }, { "0": "value5", "1": "value2", "2": 11807, "3": "30959612.92", "4": 4212, "5": "9214220.97", "6": "", "7": "", }, { "0": "value5", "1": "value3", "2": 3695, "3": "7687836.58", "4": 3565, "5": "7224948.37", "6": "", "7": "", }, { "0": "value5", "1": "value4", "2": 8112, "3": "23271776.34", "4": 647, "5": "1989272.60", "6": "", "7": "", }, { "0": "key1", "1": "value1", "2": "", "3": "", "4": 237591, "5": "473886210.33", "6": 233398, "7": "46473437976.00", }, { "0": "key1", "1": "value2", "2": "", "3": "", "4": 521, "5": "1674688.30", "6": 14244, "7": "7385070076.00", }, { "0": "key1", "1": "value3", "2": "", "3": "", "4": 1, "5": "2000.00", "6": 6151, "7": "4983865494.00", }, { "0": "key1", "1": "value4", "2": "", "3": "", "4": 520, "5": "1672688.30", "6": 8093, "7": "2401204582.00", }, ]
解决方案
要实现双向合并,需先预处理数据计算每个单元格的rowSpan和colSpan,再基于合并信息渲染页面,避免直接操作DOM的局限性。
步骤1:预处理数据,计算合并跨度
在组件中添加方法,遍历表格数据标记每个单元格的合并信息:
// 定义单元格合并信息的接口 interface CellMergeInfo { value: any; rowSpan: number; colSpan: number; isVisible: boolean; // 标记是否需要渲染该单元格 } mergedTableData: CellMergeInfo[][] = []; ngOnInit(): void { this.getColumnNames(this.tableData); this.calculateCellMerges(); } calculateCellMerges(): void { const rows = this.tableData.length; const cols = this.columnNames.length; // 初始化合并信息数组 this.mergedTableData = this.tableData.map(row => this.columnNames.map(col => ({ value: row[col], rowSpan: 1, colSpan: 1, isVisible: true })) ); // 处理垂直合并(上下相邻) for (let col = 0; col < cols; col++) { let currentRow = 0; while (currentRow < rows) { let nextRow = currentRow + 1; // 找到所有连续相同值的行 while (nextRow < rows && this.mergedTableData[nextRow][col].value === this.mergedTableData[currentRow][col].value && this.mergedTableData[nextRow][col].value !== '') { this.mergedTableData[currentRow][col].rowSpan++; this.mergedTableData[nextRow][col].isVisible = false; nextRow++; } currentRow = nextRow; } } // 处理水平合并(左右相邻) for (let row = 0; row < rows; row++) { let currentCol = 0; while (currentCol < cols) { if (!this.mergedTableData[row][currentCol].isVisible) { currentCol++; continue; } let nextCol = currentCol + 1; // 找到所有连续相同值的列 while (nextCol < cols && this.mergedTableData[row][nextCol].value === this.mergedTableData[row][currentCol].value && this.mergedTableData[row][nextCol].value !== '' && this.mergedTableData[row][nextCol].isVisible) { this.mergedTableData[row][currentCol].colSpan++; this.mergedTableData[row][nextCol].isVisible = false; nextCol++; } currentCol = nextCol; } } }
步骤2:修改HTML模板,根据合并信息渲染
<div class="table-wrapper"> <table class="fl-table" aria-describedby="table-description"> <tbody> <tr *ngFor="let row of mergedTableData"> <td *ngFor="let cell of row" [rowSpan]="cell.rowSpan" [colSpan]="cell.colSpan" *ngIf="cell.isVisible"> {{ cell.value }} </td> </tr> </tbody> </table> </div>
说明
- 先处理垂直合并,再处理水平合并,确保已被垂直合并隐藏的单元格不参与水平合并计算
- 空值单元格不参与合并,避免不必要的空白合并
- 通过
isVisible标记控制单元格是否渲染,避免重复渲染被合并的单元格
内容的提问来源于stack exchange,提问作者Purushottam Nawale
相关产品推荐
相关产品推荐

