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

AngularJS动态表头(含colspan/rowspan)关联表体实现求助

解决动态合并表头与关联tbody的问题

我明白你的困扰了——你现在的代码能完美生成带colspan和rowspan的表头,但tbody部分完全没法和表头对应,因为你复用了表头的结构数组,而tbody需要的是横向排列的每一行数据,不是表头的层级结构。咱们来一步步搞定这个问题:

问题根源

finalArrayHTML是为表头的层级结构设计的(比如"Multi 1"占一行,下面是它的两个子列),但tbody需要的是把所有叶子列(没有子列的最终列)的第n个数据项组合成一行,比如第一行是所有列的第0个值,第二行是第1个,以此类推。

解决方案步骤

1. 扁平化获取所有叶子列

先写一个递归函数,把所有最底层的、没有子列的列收集起来——这些列就是tbody每个单元格对应的数据来源:

var getLeafColumns = function(data) {
  var columns = [];
  data.forEach(function(item) {
    if (item.SubColumns.length > 0) {
      // 有子列就递归进去收集
      columns = columns.concat(getLeafColumns(item.SubColumns));
    } else {
      // 没有子列,直接加入叶子列数组
      columns.push(item);
    }
  });
  return columns;
};

2. 生成tbody的行数据

基于叶子列数组,循环生成每一行数据:

// 获取所有叶子列
var leafColumns = getLeafColumns(jsonDATA);

// 生成tbody数据:每一行是所有叶子列对应索引的值
$scope.tbodyData = [];
// 假设所有列的数据长度一致,取第一列的长度作为总行数
var totalRows = leafColumns[0].valor.length;

for (var i = 0; i < totalRows; i++) {
  var currentRow = [];
  leafColumns.forEach(function(col) {
    // 把每个叶子列的第i个值加入当前行
    currentRow.push(col.valor[i]);
  });
  $scope.tbodyData.push(currentRow);
}

3. 修改HTML中的tbody部分

把原来绑定finalArrayHTML的tbody改成绑定新生成的tbodyData:

<tbody>
  <tr data-ng-repeat="row in tbodyData">
    <td data-ng-repeat="cell in row">{{cell}}</td>
  </tr>
</tbody>

完整修改后的代码

控制器部分

angular.module('myapp', []).controller('tableColSpanRowSpan', ['$scope', function ($scope) { 
  $scope.finalArrayHTML = []; 
  var jsonDATA = [ 
    { 
      "rotulo":"Simple 1", 
      "SubColumns":[ ], 
      "valor":[ "simples 0", "simples 1", "simples 2", "simples 3", "simples 4", "simples 5", "simples 6", "simples 7", "simples 8", "simples 9" ] 
    }, 
    { 
      "rotulo":"Simple 2", 
      "SubColumns":[ ], 
      "valor":[ "simples 0", "simples 1", "simples 2", "simples 3", "simples 4", "simples 5", "simples 6", "simples 7", "simples 8", "simples 9" ] 
    }, 
    { 
      "rotulo":"Multi 1", 
      "SubColumns":[ 
        { 
          "rotulo":"Sub 1", 
          "SubColumns":[ ], 
          "valor":[ "sub 0", "sub 1", "sub 2", "sub 3", "sub 4", "sub 5", "sub 6", "sub 7", "sub 8", "sub 9" ] 
        }, 
        { 
          "rotulo":"Sub 2", 
          "SubColumns":[ ], 
          "valor":[ "sub 0", "sub 1", "sub 2", "sub 3", "sub 4", "sub 5", "sub 6", "sub 7", "sub 8", "sub 9" ] 
        } 
      ] 
    } 
  ]; 

  // 原有的表头生成逻辑,保留不变
  var getColSpan = function (data, count) { 
    if (data.length > 0) { 
      for (var c = 0; c < data.length; c++) { 
        if (data[c].SubColumns.length > 0) { 
          count = getColSpan(data[c].SubColumns, count); 
        } else { 
          ++count; 
        } 
      } 
    } 
    return count; 
  }; 

  var getDepth = function (obj) { 
    var depth = 0; 
    if (obj.SubColumns) { 
      obj.SubColumns.forEach(function (d) { 
        var tmpDepth = getDepth(d) 
        if (tmpDepth > depth) { 
          depth = tmpDepth 
        } 
      }) 
    } 
    return 1 + depth 
  }; 

  var getRowSpan = function (allData, index, count) { 
    if (allData[index].SubColumns.length > 0) { 
    } else { 
      var depth = 0; 
      for (var j = 0; j < allData.length; j++) { 
        var depthTmp = getDepth(allData[j]); 
        if (depthTmp > depth) { 
          depth = depthTmp; 
        } 
      } 
      return depth; 
    } 
    return count; 
  }; 

  var rowIndex = 0; 
  var RecursiveFunction = function (data, currentRow) { 
    for (var i = 0; i < data.length; i++) { 
      var tmp = { 
        title: data[i].rotulo, 
        conteudo: data[i].valor, 
        colSpan: getColSpan(data[i].SubColumns, 0), 
        rowSpan: getRowSpan(data, i, 0) 
      }; 
      if (typeof $scope.finalArrayHTML[rowIndex] == 'undefined') { 
        $scope.finalArrayHTML[rowIndex] = []; 
      } 
      $scope.finalArrayHTML[rowIndex].push(tmp); 
      if (data[i].SubColumns.length > 0) { 
        ++rowIndex; 
        RecursiveFunction(data[i].SubColumns, data[i]); 
        --rowIndex; 
      } 
    } 
  }; 

  RecursiveFunction(jsonDATA, 0); 

  // 新增的tbody数据生成逻辑
  var getLeafColumns = function(data) {
    var columns = [];
    data.forEach(function(item) {
      if (item.SubColumns.length > 0) {
        columns = columns.concat(getLeafColumns(item.SubColumns));
      } else {
        columns.push(item);
      }
    });
    return columns;
  };

  var leafColumns = getLeafColumns(jsonDATA);
  $scope.tbodyData = [];
  var totalRows = leafColumns[0].valor.length;

  for (var i = 0; i < totalRows; i++) {
    var currentRow = [];
    leafColumns.forEach(function(col) {
      currentRow.push(col.valor[i]);
    });
    $scope.tbodyData.push(currentRow);
  }
}]); 

HTML部分

<!doctype html> 
<html ng-app="myapp"> 
<head> 
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular.js"></script> 
</head> 
<body> 
<div ng-controller="tableColSpanRowSpan"> 
<table border="1" cellspacing="2" cellpadding="3"> 
<thead> 
<tr data-ng-repeat="t in finalArrayHTML"> 
<th colspan="{{h.colSpan}}" rowspan="{{h.rowSpan}}" data-ng-repeat="h in t">{{h.title}}</th> 
</tr> 
</thead> 
<tbody> 
<tr data-ng-repeat="row in tbodyData">
  <td data-ng-repeat="cell in row">{{cell}}</td>
</tr> 
</tbody> 
</table> 
</div> 
</body> 
</html>

这样修改后,表头的合并单元格正常显示,tbody的每一行数据也会和表头的列一一对应,完美解决你的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:58:16