如何在没有 html 元素的情况下使用 n- 重复

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

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

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

例如,如果我在 <span>上使用 n- 重复,我会得到:

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

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

我怎样才能使用 n- 重复这样的方式,它被绑定到的 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>
103314 次浏览
<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>

我认为这是有效的:)

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

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.2 + ,使用 重复,启动

不然的话,在身体上加个“重复”怎么样?(AFAIK,在一个表中有多个 < tbody > s 是可以的。)

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

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

如果使用 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-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。

应该能打印出来

Name1,name1 _ 1,name1 _ 2,name2,name2 _ 1,name2 _ 2,

我只是想评论一下,但是我的名声还是很差。所以我加入了另一个解决方案,也解决了这个问题。我真的很想反驳@bmoeskau 的观点,即解决这个问题需要一个“最好的拙劣”的解决方案,因为这是最近在一次讨论中提出的,尽管这篇文章已经发表了两年,我想加上我自己的两点意见:

正如@btford 所指出的,您似乎试图将一个递归结构转换为一个列表,因此您应该首先将该结构转换为一个列表。他的解决方案可以做到这一点,但是有一种观点认为在模板中调用函数是不优雅的。如果这是真的(老实说,我不知道) ,是否只需要执行控制器中的函数而不是指令?

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

角度指令并不缺乏功能。它们只是强制您编写有效的 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']

这将适用于任何具有子项的树状结构。如果你想要整个项目而不是一个属性,你可以缩短扁平化功能甚至更多。

希望能帮上忙。

找到一个真正有效的解决方案

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

然后,自定义 move 指令将 <remove>开始和结束元素与 <!--removed element-->放在一起