2012-07-15 13 views
126

Potrzebuję użyć ng-repeat (w AngularJS), aby wyświetlić listę wszystkich elementów w tablicy.Jak używać ng-repeat bez elementu html

Powikłaniem jest to, że każdy element tablicy zostanie przekształcony do jednego, dwóch lub trzech wierszy tabeli.

Nie mogę utworzyć poprawnego html, jeśli w elemencie użyto elementu ng-repeat, ponieważ nie jest dozwolony żaden typ powtarzalnego elementu między <tbody> i <tr>.

Na przykład, jeśli użyłem NG-repeat na <span>, chciałbym dostać:

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

Która jest nieprawidłowy HTML.

Ale co muszę być generowana jest:

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

gdzie pierwszy rząd został wygenerowany przez pierwszy element tablicy, następne trzy przez drugi i piąty i szósty przez ostatniego elementu tablicy.

Jak korzystać z polecenia ng-repeat w taki sposób, że element HTML, z którym jest związany, "znika" podczas renderowania?

Czy istnieje inne rozwiązanie tego problemu?


Wyjaśnienie: Wygenerowana struktura powinna wyglądać jak poniżej. Każdy element tablicy może generować od 1 do 3 wierszy tabeli. Odpowiedź powinna idealnie wspierać 0-n wierszy na element tablicy.

<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

Może powinieneś użyć "replace: true"? Zobacz: http: // stackoverflow.com/questions/11426114/angularjs-why-doesnt-replace-true-work-with-templateurl-property – Tommy

+0

Ponadto, dlaczego nie możesz użyć polecenia ng-repeat na samym pliku tr? – Tommy

+2

@Tommy, ponieważ "każdy element tablicy przekształci się w jeden, dwa lub trzy wiersze tabeli". Gdybym użył powtórzenia ng na 'tr', dostałbym jeden wiersz na element tablicy, o ile rozumiem. – fadedbee

Odpowiedz

59

Aktualizacja: Jeśli używasz Angulara 1.2+, użyj ng-repeat-start. Zobacz odpowiedź @ jmagnusson.

W przeciwnym razie, jak wstawić powtórzenie ng na tbody? (AFAIK, to jest w porządku, aby mieć wiele < tbody> s w jednej tabeli.)

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

Chociaż może to technicznie zadziałać, to bardzo rozczarowujące, że odpowiedź na to powszechne przypadki użycia polega na tym, że trzeba wstrzykiwać dowolne (w przeciwnym razie niepotrzebne) znaczniki. Mam ten sam problem (powtarzające się grupy wierszy - jeden nagłówek TR z jednym lub kilkoma dziećmi TR, powtarzany jako grupa). Trivial z innymi silnikami szablonów, w najlepszym wypadku hakerski z Angularem. –

+1

Uzgodnione. Próbuję powtórzyć elementy DT + DD. nie ma możliwości zrobienia tego bez dodania nieprawidłowego elementu owijającego – DavidLin

+2

@ DavidLin, w Angular v1.2 (gdy tylko się pojawi) będziesz mógł powtarzać wiele elementów, np. dt i dd: http: // www. youtube.com/watch?v=W13qDdJDHp8&t=17m28s –

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

myślę, że to jest ważne :)

+0

Gdy to działa, działa tylko wtedy, gdy tablica ma trzy elementy. – btford

+0

Upewnij się tylko, że tablica ma 3 elementy, nawet jeśli są pustymi tablicami (ng-repeat z pustą tablicą po prostu nie renderuj niczego). –

+7

Chodzi mi o to, że OP prawdopodobnie chce rozwiązania, które działa dla zmiennej liczby elementów w tablicy. Zakładam, że kodowanie "musi zawierać trzy elementy w tej tablicy" w szablonie byłoby złym rozwiązaniem. – btford

17

Może chcesz spłaszczyć danych w kontrolerze:

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

A potem w HTML:

<table> 
    <tbody> 
    <tr ng-repeat="item in flattened()"><td>{{item}}</td></tr> 
    </tbody> 
</table> 
+5

W szerszym kontekście jest to właściwa odpowiedź. – nilskp

+1

Nie, nie jest. Wywołanie funkcji wewnątrz wyrażenia ngRepeat absolutnie nie jest zalecane. –

0

można użyć podkreślenia funkcja spłaszczyć $scope.myData= _.flatten($scope.myData);

66

As of angularjs 1.2 istnieje dyrektywa zwana ng-repeat-start że robi dokładnie o co prosisz. Zobacz moją odpowiedź in this question, aby uzyskać opis, jak z niej korzystać.

+0

Angular dogonił, to jest teraz właściwe rozwiązanie. – iwein

25

Jeśli używasz ng> 1.2, tutaj jest przykład użycia ng-repeat-start/end bez generowania niepotrzebnych tagów:

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

Ważnym punktem: dla znaczników używanych do ng-repeat-start i ng-repeat-end ustawić ng-if="0", aby nie pozwolić zostać wstawiony na stronie. W ten sposób wewnętrzna zawartość będzie obsługiwana dokładnie tak, jak jest w knockoutjs (używając poleceń w <!--...-->) i nie będzie żadnych śmieci.

+0

po ustawieniu ng-if = "0", wyświetla komunikat o błędzie z informacją, że nie można znaleźć pasującego ng-powtórzenie. –

3

Powyższe informacje są poprawne, ale dla bardziej ogólnej odpowiedzi nie wystarczy. Musiałem zagnieździć się ng-repeat, ale pozostać na tym samym poziomie html, czyli napisać elementy w tym samym rodzica. Tablica tagów zawiera znaczniki, które również mają tablicę znaczników. To jest właściwie drzewo.

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

Oto, co ostatecznie zrobiłem.

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

Zauważ, że ng-if = "false" ukrywa początek i koniec elementu div.

Należy wydrukować

nazwa1, name1_1, name1_2, name2, name2_1, name2_2,

1

Chciałbym tylko skomentować, ale moja reputacja jest nadal brakuje. Dodaję więc kolejne rozwiązanie, które również rozwiązuje problem. Naprawdę chciałbym odeprzeć oświadczenie poczynione przez @boesai, że rozwiązanie tego problemu wymaga "hacky w najlepszym" rozwiązaniu, a ponieważ pojawił się niedawno w dyskusji, chociaż ten post ma 2 lata, chciałbym dodać własne dwa centy:

Jak zaznaczył @btford, wydajesz się próbować przekształcić strukturę rekurencyjną na listę, więc powinieneś najpierw spłaszczyć tę strukturę do listy. Jego rozwiązanie to robi, ale jest opinia, że ​​wywoływanie funkcji wewnątrz szablonu jest nieeleganckie. jeśli to prawda (szczerze mówiąc, nie wiem) czy nie wymagałoby to wykonania funkcji w kontrolerze, a nie dyrektywy?

tak czy inaczej, twój html wymaga listy, więc zakres, który jest renderowany, powinien mieć tę listę do pracy. po prostu trzeba spłaszczyć strukturę wewnątrz kontrolera. gdy masz już tablicę $ scope.rows, możesz wygenerować tabelę za pomocą pojedynczego, prostego powtórzenia ng. Bez hackowania, bez ingerencji, po prostu w taki sposób, w jaki został zaprojektowany.

W dyrektywach Angulars nie brakuje funkcjonalności. Po prostu zmuszają cię do napisania poprawnego html. Mój kolega miał podobny problem, powołując się na @boeskau w celu poparcia krytyki dotyczącej cech szablonów/renderingów. Kiedy przyjrzeliśmy się dokładnemu problemowi, okazało się, że po prostu chciał wygenerować open-tag, a następnie zamknąć tag gdzieś indziej, itd., Podobnie jak w dawnych dobrych czasach, kiedy moglibyśmy połączyć nasz kod HTML ze stringami ... prawda? Nie.

jak spłaszczenie struktury na listę, oto inne rozwiązanie:

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

to będzie działać na dowolnej strukturze drzewiastej, która ma podrzędne elementy. Jeśli chcesz mieć cały przedmiot zamiast własności, możesz jeszcze bardziej skrócić funkcję spłaszczania.

nadzieję, że pomaga.

+0

Niezły. Chociaż nie pomogło mi to bezpośrednio, otworzyło mi to umysł na inne rozwiązanie. Wielkie dzięki! –