如何在没有html元素的情况下使用ng-repeat

时间:2012-07-15 09:49:57

标签: angularjs

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

复杂的是,数组的每个元素都将转换为表的一行,两行或三行。

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

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

<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>          

8 个答案:

答案 0 :(得分:71)

从AngularJS 1.2开始,有一个名为ng-repeat-start的指令完全符合您的要求。有关如何使用它的说明,请参阅我的回答in this question

答案 1 :(得分:64)

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

否则,如何将ng-repeat放在tbody上? (AFAIK,可以在一个表中包含多个&lt; tbody&gt ;.)

<tbody ng-repeat="row in array">
  <tr ng-repeat="item in row">
     <td>{{item}}</td>
  </tr>
</tbody>

答案 2 :(得分:30)

如果您使用ng&gt; 1.2,这是一个使用ng-repeat-start/end而不生成不必要的标签的例子:

<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-end设置ng-if="0"的标记,不要在页面中插入。通过这种方式,内部内容将与knockoutjs中的内容完全一样(使用<!--...-->中的命令),并且不会有垃圾。

答案 3 :(得分:18)

您可能希望压缩控制器中的数据:

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>

答案 4 :(得分:1)

我想发表评论,但我的声誉仍然缺乏。所以我正在添加另一个解决问题的解决方案。我真的想驳斥@bmoeskau所说的解决这个问题需要一个“hacky at best”解决方案的声明,并且由于最近在讨论中出现了这个帖子,即使这个帖子是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']

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

希望有所帮助。

答案 5 :(得分:0)

您可以使用下划线拼合功能$scope.myData= _.flatten($scope.myData);

答案 6 :(得分: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块,而不仅仅是一个元素。 / p>

然后,定制remove指令将<remove>的开始和结束元素置于<!--removed element-->

答案 7 :(得分:-1)

<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>

我认为这是有效的:)