2012-07-15 19 views
126

我需要使用ng-repeat(在AngularJS)列出所有在數組中的元素的。如何使用NG-不重複的HTML元素

的複雜性在於,陣列中的每個元素將變換到一個表中的任何一個,兩個或三個行。

我不能創建有效的HTML,如果ng-repeat使用的元件上,因爲沒有類型的重複元件的被允許<tbody><tr>之間。

例如,如果我用<span> NG重複,我會得到:

<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-重複這樣的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>   
+0

也許你應該使用「replace:true」?看到這個:http:// stackoverflow。com/questions/11426114/angularjs-why-doesnt-replace-true-work-with-templateurl-property – Tommy 2012-07-15 11:40:01

+0

此外,爲什麼你不能在tr本身上使用ng-repeat? – Tommy 2012-07-15 12:55:20

+2

@Tommy,因爲「數組的每個元素將轉換爲表的一個,兩個或三個行」。據我所知,如果我在'tr'上使用了ng-repeat,每個數組元素會得到一行。 – fadedbee 2012-07-15 13:37:27

回答

59

更新:如果您使用的是Angular 1.2+,請使用ng-repeat-start。請參閱@ jmagnusson的答案。

否則,如何將ng-repeat放在tbody上? (據我所知,它是好的,有多個< TBODY> S IN的單表。)

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

雖然這可能在技術上起作用,但是對於這個常見用例的答案是非常令人失望的,那就是你必須注入任意的(不必要的)標記。我有同樣的問題(重複的行組 - 一個標題TR與一個或多個子TR,作爲一個組重複)。與其他模板引擎瑣碎,似乎與Angular最好的。 – 2013-06-17 07:53:09

+1

同意。我正在嘗試重複DT + DD元素。如果沒有添加無效的包裝元素 – DavidLin 2013-07-18 00:18:35

+2

@DavidLin,在Angular v1.2中(無論何時出現),您都可以重複使用多個元素,例如dt和dd:http:// www。 youtube.com/watch?v=W13qDdJDHp8&t=17m28s – 2013-07-18 13:50:50

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

我認爲這是有效的:)

+0

雖然這個工作,它只會在數組有三個元素時才起作用。 – btford 2012-07-15 23:32:22

+0

只要確保數組有3個元素,即使它們是空數組(ng-重複一個空數組也不會渲染任何東西)。 – 2012-07-16 00:54:44

+7

我的觀點是,OP可能需要一個適用於數組中可變數量項目的解決方案。我假設硬編碼「模板中必須有三個項目」將是一個糟糕的解決方案。 – btford 2012-07-16 02:49:01

17

您可能希望你的控制器內拼合數據:

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

從更廣泛的角度來看,這是正確的答案。 – nilskp 2012-11-20 15:30:54

+1

不,不是。在ngRepeat表達式中調用一個函數是絕對不推薦的 – 2015-08-04 09:53:30

0

您可以使用下劃線扁平化功能$scope.myData= _.flatten($scope.myData);

66

由於AngularJS 1.2有一個名爲ng-repeat-start指令,不正是你要求什麼。有關如何使用它的說明,請參閱我的回答in this question

+0

Angular已經趕上了,這是現在正確的解決方案。 – iwein 2014-06-23 22:51:08

25

如果使用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",讓不在頁被插入。通過這種方式,內部內容將與knockoutjs中的內容完全相同(使用<!--...-->中的命令),並且不會有垃圾。

+0

設置ng-if =「0」後,會拋出錯誤提示無法找到匹配的ng-repeat-end。 – 2017-03-30 14:25:08

3

以上是正確的,但對於更一般的答案是不夠的。我需要嵌套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,name1_1,name1_2,NAME2 NAME2_1,name2_2,

1

我想只發表評論,但我的名聲仍然缺乏。所以我添加了解決問題的另一個解決方案。我真的想反駁@ bmoeskau所做的聲明,即解決這個問題需要一個'hacky at best'解決方案,並且由於這個問題最近在討論中出現,即使這篇文章已經2年了,我想添加我的擁有兩美分:

正如@btford指出的那樣,您似乎試圖將遞歸結構轉換爲列表,因此您應該先將該結構扁平化爲列表。他的解決方案就是這樣做的,但有一種觀點認爲,調用模板內部的函數並不雅觀。如果這是真的(老實說,我不知道)wouldnt只需要執行控制器中的功能,而不是指令?

無論哪種方式,你的html需要一個列表,所以呈現它的範圍應該有該列表來處理。你只需要將控制器內部的結構弄平。一旦你有一個$ scope.rows數組,你可以用一個簡單的ng-repeat生成表格。沒有黑客,沒有不雅,只是它設計的工作方式。

Angulars指令並不缺乏功能。他們只是強迫你寫出有效的html。我的一位同事也有類似的問題,引用@ bmoeskau來支持對angulars模板/渲染特性的批評。當看到確切的問題時,事實證明他只是想生成一個開放標籤,然後在其他地方生成一個關閉標籤等等。就像在我們將字符串連接到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'] 

這會爲任何樹狀結構包含子項工作。如果您想要整個項目而不是屬性,則可以更加縮短展平功能。

希望有幫助。

+0

不錯的。雖然它並沒有直接幫助我,但它讓我開始思考另一種解決方案。非常感謝! – 2015-12-30 16:29:05