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


142

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

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

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

例如,如果在上使用ng-repeat <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>          

也许您应该使用“ replace:true”?看到这一点:stackoverflow.com/questions/11426114/...

另外,为什么不能在tr本身上使用ng-repeat?

2
@Tommy,因为“数组的每个元素都将转换为表的一,两或三行”。tr据我了解,如果我在ng-repeat上使用,我将为每个数组元素获取一行。
褪色的蜜蜂

1
好的我明白了。您不能在转发器中使用模型之前将其展平吗?

@汤米,不。1-3 tr被一个数组元素生成S-不具有相同的结构。
fadebee

Answers:


66

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

否则,如何将ng-repeat放在身上?(AFAIK,可以在单个表中包含多个<tbody>。)

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

62
尽管从技术上讲这可能可行,但是对于这种常见用例的答案是,您必须注入任意(否则是不必要的)标记,这非常令人失望。我有同样的问题(重复的行组-一个头TR和一个或多个子TR,作为一组重复)。与其他模板引擎无关紧要,似乎对Angular而言最多是hacky。
Brian Moeskau 2013年

1
同意 我正在尝试对DT + DD元素进行重复。没有添加无效的包装元素就无法做到这一点
David Lin

2
@DavidLin,在Angular v1.2中(无论何时发布),您都可以重复多个元素,例如dt和dd:youtube.com/watch?
v=W13qDdJDHp8&t=17m28s

使用无容器控制流语法在KnockoutJS中对此进行了优雅的处理:tickoutjs.com/documentation/foreach-binding.html。希望看到Angular尽快做类似的事情。
Cory House

3
此答案已过时,请改用ng-repeat-start
iwein14年

73

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


Angular赶上了,这是现在的正确解决方案。
iwein 2014年

33

如果您使用ng> 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",不要插入页面。这样,内部内容将被完全像剔除js中一样处理(使用中的命令<!--...-->),并且不会有垃圾。


设置ng-if =“ 0”后,它引发错误,提示无法找到匹配的ng-repeat-end。
vikky MCTS

19

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

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>

1
不,这不对。绝对不建议在ngRepeat表达式内调用函数
Nik

就我而言,我确实需要添加一个分隔符为每个元素在指数= 0! ng-repeat-startng-repeat-end打破我的设计,因此该解决方案是创建一个额外的变量的每个元素之前添加该分离对象和迭代的新变种: <div class="c477_group"> <div class="c477_group_item" ng-repeat="item in itemsWithSeparator" ng-switch="item.id" ng-class="{'-divider' : item.id == 'SEPARATOR'}"> <div class="c478" ng-switch-when="FAS"/> <div class="c478" ng-switch-when="SEPARATOR" /> <div class="c479" ng-switch-default /> </div> </div>
hermeslm

6

以上是正确的,但对于更一般的答案是不够的。我需要嵌套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>

注意ng-if =“ false”隐藏了开始和结束div。

它应该打印

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


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

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

希望能有所帮助。


好一个。虽然它并不能直接帮助我,但确实让我打开了另一个解决方案的大门。非常感谢!
玛丽安(Marian Zburlea)2015年

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指令将<remove>start和finish元素放置在<!--removed element-->


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

我认为这是有效的:)


在这种情况下,只有在数组包含三个元素的情况下,它才会起作用。
btford

只要确保数组具有3个元素,即使它们是空数组(带有空数组的ng-repeat根本不呈现任何内容)。
Renan Tomal Fernandes 2012年

7
我的观点是,OP可能需要一种适用于数组中可变数量项的解决方案。我认为将“在此数组中必须有三个项目”硬编码到模板中将是一个糟糕的解决方案。
btford

在对他的问题的最后评论中,他说元素不会具有相同的结构,因此对每个结构进行硬编码是不可避免的。
Renan Tomal Fernandes 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.