148

我需要使用ng-repeat(在 AngularJS 中)列出数组中的所有元素。

复杂之处在于,数组的每个元素都将转换为表格的一、二或三行。

如果在元素上使用,我无法创建有效的 html,因为和ng-repeat之间不允许有任何类型的重复元素。<tbody><tr>

例如,如果我使用 ng-repeat on <span>,我会得到:

<table>
  <tbody>
    <span>
      <tr>...</tr>
    </span>
    <span>
      <tr>...</tr>
      <tr>...</tr>
      <tr>...</tr>
    </span>
    <span>
      <tr>...</tr>
      <tr>...</tr>
    </span>
  </tbody>
</table>          

这是无效的html。

但我需要生成的是:

<table>
  <tbody>
    <tr>...</tr>
    <tr>...</tr>
    <tr>...</tr>
    <tr>...</tr>
    <tr>...</tr>
    <tr>...</tr>
  </tbody>
</table>          

其中第一行由第一个数组元素生成,接下来的三行由第二个数组元素生成,第五和第六行由最后一个数组元素生成。

如何使用 ng-repeat 使其绑定的 html 元素在渲染期间“消失”?

或者有其他解决方案吗?


说明:生成的结构应如下所示。每个数组元素可以生成 1-3 行的表格。理想情况下,答案应该支持每个数组元素 0-n 行。

<table>
  <tbody>
    <!-- array element 0 -->
    <tr>
      <td>One row item</td>
    </tr>
    <!-- array element 1 -->
    <tr>
      <td>Three row item</td>
    </tr>
    <tr>
      <td>Some product details</td>
    </tr>
    <tr>
      <td>Customer ratings</td>
    </tr>
    <!-- array element 2 -->
    <tr>
      <td>Two row item</td>
    </tr>
    <tr>
      <td>Full description</td>
    </tr>
  </tbody>
</table>          
4

8 回答 8

71

从 AngularJS 1.2 开始,有一个指令ng-repeat-start可以完全满足您的要求。有关如何使用它的描述,请参阅我在这个问题中的回答。

于 2013-09-17T06:57:00.783 回答
68

更新:如果您使用的是 Angular 1.2+,请使用ng-repeat-start。请参阅@jmagnusson 的回答。

否则,如何将 ng-repeat 放在 tbody 上?(AFAIK,在一个表中有多个 <tbody> 是可以的。)

<tbody ng-repeat="row in array">
  <tr ng-repeat="item in row">
     <td>{{item}}</td>
  </tr>
</tbody>
于 2012-08-16T20:39:49.557 回答
37

ng-repeat-start/end如果您使用 ng > 1.2,这里是一个不生成不必要标签的使用示例:

<html>
  <head>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
    <script>
      angular.module('mApp', []);
    </script>
  </head>
  <body ng-app="mApp">
    <table border="1" width="100%">
      <tr ng-if="0" ng-repeat-start="elem in [{k: 'A', v: ['a1','a2']}, {k: 'B', v: ['b1']}, {k: 'C', v: ['c1','c2','c3']}]"></tr>

      <tr>
        <td rowspan="{{elem.v.length}}">{{elem.k}}</td>
        <td>{{elem.v[0]}}</td>
      </tr>
      <tr ng-repeat="v in elem.v" ng-if="!$first">
        <td>{{v}}</td>
      </tr>

      <tr ng-if="0" ng-repeat-end></tr>
    </table>
  </body>
</html>

重要的一点:对于用于ng-repeat-startng-repeat-endset的标签ng-if="0",不要插入到页面中。这样内部的内容就会完全按照knockoutjs中的方式处理(使用in中的命令<!--...-->),不会有垃圾。

于 2015-07-10T15:09:08.107 回答
20

您可能希望展平控制器中的数据:

function MyCtrl ($scope) {
  $scope.myData = [[1,2,3], [4,5,6], [7,8,9]];
  $scope.flattened = function () {
    var flat = [];
    $scope.myData.forEach(function (item) {
      flat.concat(item);
    }
    return flat;
  }
}

然后在 HTML 中:

<table>
  <tbody>
    <tr ng-repeat="item in flattened()"><td>{{item}}</td></tr>
  </tbody>
</table>
于 2012-07-15T19:23:22.793 回答
9

以上是正确的,但对于更一般的答案是不够的。我需要嵌套 ng-repeat,但保持在同一个 html 级别,这意味着将元素写入同一个父级。标签数组包含标签,这些标签也有标签数组。它实际上是一棵树。

[{ name:'name1', tags: [
  { name: 'name1_1', tags: []},
  { name: 'name1_2', tags: []}
  ]},
 { name:'name2', tags: [
  { name: 'name2_1', tags: []},
  { name: 'name2_2', tags: []}
  ]}
]

所以这就是我最终所做的。

<div ng-repeat-start="tag1 in tags" ng-if="false"></div>
    {{tag1}},
  <div ng-repeat-start="tag2 in tag1.tags" ng-if="false"></div>
    {{tag2}},
  <div ng-repeat-end ng-if="false"></div>
<div ng-repeat-end ng-if="false"></div>

请注意隐藏开始和结束 div 的 ng-if="false"。

它应该打印

名称 1,名称 1_1,名称 1_2,名称 2,名称 2_1,名称 2_2,

于 2015-08-14T12:25:58.410 回答
1

我只想发表评论,但我的声誉仍然不足。所以我正在添加另一个解决问题的解决方案。我真的很想反驳@bmoeskau 的说法,即解决这个问题需要一个“最好的黑客”解决方案,并且由于最近在讨论中提出了这个问题,即使这篇文章已经有 2 年了,我想添加我的拥有两分钱:

正如@btford 指出的那样,您似乎正在尝试将递归结构转换为列表,因此您应该首先将该结构展平为列表。他的解决方案是这样做的,但有人认为在模板内调用函数是不优雅的。如果这是真的(老实说,我不知道)那不只需要在控制器中执行函数而不是指令吗?

无论哪种方式,您的 html 都需要一个列表,因此呈现它的范围应该有该列表可以使用。您只需将控制器内部的结构展平即可。一旦你有了一个 $scope.rows 数组,你就可以用一个简单的 ng-repeat 生成表。没有黑客,没有不雅,只是它被设计的工作方式。

Angulars 指令并不缺少功能。他们只是强迫你编写有效的 html。我的一位同事也有类似的问题,他引用@bmoeskau 来支持对角度模板/渲染功能的批评。当查看确切的问题时,结果发现他只是想生成一个打开标签,然后在其他地方生成一个关闭标签,等等。就像在过去我们从字符串连接我们的 html 的美好时光......对吧?不。

至于将结构展平为列表,这是另一种解决方案:

// assume the following structure
var structure = [
    {
        name: 'item1', subitems: [
            {
                name: 'item2', subitems: [
                ],
            }
        ],
    }
];
var flattened = structure.reduce((function(prop,resultprop){
    var f = function(p,c,i,a){
        p.push(c[resultprop]);
        if (c[prop] && c[prop].length > 0 )
          p = c[prop].reduce(f,p);
        return p;
    }
    return f;
})('subitems','name'),[]);

// flattened now is a list: ['item1', 'item2']

这适用于任何具有子项的树状结构。如果您想要整个项目而不是属性,则可以进一步缩短展平功能。

希望有帮助。

于 2015-09-30T10:09:00.080 回答
0

真正有效的解决方案

html

<remove  ng-repeat-start="itemGroup in Groups" ></remove>
   html stuff in here including inner repeating loops if you want
<remove  ng-repeat-end></remove>

添加一个 angular.js 指令

//remove directive
(function(){
    var remove = function(){

        return {    
            restrict: "E",
            replace: true,
            link: function(scope, element, attrs, controller){
                element.replaceWith('<!--removed element-->');
            }
        };

    };
    var module = angular.module("app" );
    module.directive('remove', [remove]);
}());

简要说明,

ng-repeat 将自身绑定到<remove>元素并循环,因为我们使用了 ng-repeat-start / ng-repeat-end 它循环一个 html 块而不仅仅是一个元素。

然后自定义删除指令将<remove>开始和结束元素放置在<!--removed element-->

于 2019-07-20T22:35:51.840 回答
-2
<table>
  <tbody>
    <tr><td>{{data[0].foo}}</td></tr>
    <tr ng-repeat="d in data[1]"><td>{{d.bar}}</td></tr>
    <tr ng-repeat="d in data[2]"><td>{{d.lol}}</td></tr>
  </tbody>
</table>

我认为这是有效的:)

于 2012-07-15T16:31:57.653 回答