Jak używać ng-repeat bez elementu HTML

142

Muszę użyć ng-repeat(w AngularJS), aby wyświetlić wszystkie elementy w tablicy.

Trudność polega na tym, że każdy element tablicy przekształci się w jeden, dwa lub trzy wiersze tabeli.

Nie mogę utworzyć prawidłowego kodu HTML, jeśli ng-repeatjest używany w elemencie, ponieważ żaden typ powtarzającego się elementu nie jest dozwolony między <tbody>a <tr>.

Na przykład, gdybym użył ng-repeat on <span>, dostałbym:

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

Który jest nieprawidłowym html.

Ale to, czego potrzebuję, to:

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

gdzie pierwszy wiersz został wygenerowany przez pierwszy element tablicy, następne trzy przez drugi, a piąty i szósty przez ostatni element tablicy.

Jak mogę użyć ng-repeat w taki sposób, aby element html, z którym jest powiązany, „znikał” podczas renderowania?

Czy jest na to inne rozwiązanie?


Wyjaśnienie: Wygenerowana struktura powinna wyglądać jak poniżej. Każdy element tablicy może generować 1-3 wiersze tabeli. W idealnym przypadku odpowiedź powinna obsługiwać od 0 do 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>          
fadedbee
źródło
Może powinieneś użyć „replace: true”? Zobacz to: stackoverflow.com/questions/11426114/ ...
Poza tym, dlaczego nie możesz użyć ng-repeat na samym tr?
2
@Tommy, ponieważ „każdy element tablicy zostanie przekształcony w jeden, dwa lub trzy wiersze tabeli”. Gdybym użył ng-repeat na tr, dostałbym jeden wiersz na element tablicy, o ile rozumiem.
fadedbee
1
Dobra, widzę. Czy nie możesz po prostu spłaszczyć modelu przed użyciem go w repeaterze?
@Tommy, nie. 1-3, trktóre są generowane przez jeden element tablicy, nie mają takiej samej struktury.
fadedbee

Odpowiedzi:

66

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

W przeciwnym razie, co powiesz na umieszczenie powtórzenia ng na ciele? (AFAIK, dobrze jest mieć wiele elementów <tbody> w jednej tabeli).

<tbody ng-repeat="row in array">
  <tr ng-repeat="item in row">
     <td>{{item}}</td>
  </tr>
</tbody>
Mark Rajcok
źródło
62
Chociaż technicznie może to zadziałać, bardzo rozczarowujące jest to, że odpowiedzią na ten typowy przypadek użycia jest wstrzyknięcie dowolnego (w przeciwnym razie niepotrzebnego) znacznika. Mam ten sam problem (powtarzające się grupy wierszy - jeden nagłówek TR z jednym lub większą liczbą podrzędnych TR, powtarzany jako grupa). Banalne z innymi silnikami szablonów, wydaje się, że w najlepszym przypadku jest hacky z Angularem.
Brian Moeskau
1
Zgoda. Próbuję powtórzyć elementy DT + DD. nie da się tego zrobić bez dodania nieprawidłowego elementu opakowującego
David Lin,
2
@DavidLin, w Angular v1.2 (kiedy tylko się pojawi) będziesz mógł powtórzyć wiele elementów, np.
Dt
Jest to obsługiwane elegancko w KnockoutJS przy użyciu składni bezkontenerowego przepływu sterowania: knockoutjs.com/documentation/foreach-binding.html . Mam nadzieję, że Angular wkrótce zrobi coś podobnego.
Cory House
3
Ta odpowiedź jest nieaktualna, zamiast tego użyj ng-repeat-start
iwein
73

Od AngularJS 1.2 istnieje dyrektywa o nazwie, ng-repeat-startktóra robi dokładnie to, o co prosisz. Zobacz moją odpowiedź na to pytanie, aby zapoznać się z opisem, jak go używać.

jmagnusson
źródło
Angular dogonił, to jest teraz właściwe rozwiązanie.
iwein
33

Jeśli używasz ng> 1.2, oto przykład użycia ng-repeat-start/endbez 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żna kwestia: w przypadku tagów używanych ng-repeat-starti ng-repeat-endustawianych ng-if="0", nie wolno ich wstawiać na stronę. W ten sposób zawartość wewnętrzna będzie obsługiwana dokładnie tak, jak w knockoutjs (przy użyciu poleceń w <!--...-->) i nie będzie żadnych śmieci.

Duka Árpád
źródło
po ustawieniu ng-if = "0", generuje błąd informujący, że nie można znaleźć pasującego końca powtórzenia ng.
Vikky MCTS
19

Możesz chcieć spłaszczyć dane w swoim 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>
btford
źródło
1
Nie, nie jest. Wywoływanie funkcji wewnątrz wyrażenia ngRepeat nie jest absolutnie zalecane
Nik,
W moim przypadku, nie trzeba dodawać separatora dla każdego elementu gdzie indeks = 0 i! ng-repeat-start, ng-repeat-endZłamać mój projekt, więc rozwiązaniem było stworzenie dodatkowej zmiennej dodanie tego obiektu separatora przed każdym elemencie i iteracyjne nowy var: <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

Powyższe jest poprawne, ale aby uzyskać bardziej ogólną odpowiedź, nie wystarczy. Musiałem zagnieżdżać ng-repeat, ale pozostać na tym samym poziomie html, czyli pisać elementy w tym samym rodzicu. Tablica tagów zawiera znaczniki, które również mają tablicę tagów. W rzeczywistości jest to 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 w końcu 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>

Zwróć uwagę na ng-if = "false", które ukrywa początkowe i końcowe elementy div.

Powinien się wydrukować

nazwa1, nazwa1_1, nazwa1_2, nazwa2, nazwa2_1, nazwa2_2,

Αλέκος
źródło
1

Chciałbym tylko skomentować, ale wciąż brakuje mojej reputacji. Więc dodaję inne rozwiązanie, które również rozwiązuje problem. Naprawdę chciałbym obalić stwierdzenie @bmoeskau, że rozwiązanie tego problemu wymaga co najwyżej hakerskiego rozwiązania, a ponieważ pojawiło się to niedawno w dyskusji, mimo że ten post ma 2 lata, chciałbym dodać moje posiadać dwa centy:

Jak zauważył @btford, wydaje się, że próbujesz przekształcić strukturę rekurencyjną w listę, więc najpierw powinieneś spłaszczyć tę strukturę do postaci listy. Jego rozwiązanie to robi, ale istnieje opinia, że ​​wywołanie 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 kod HTML wymaga listy, więc zakres, który renderuje, powinien mieć tę listę do pracy. wystarczy spłaszczyć strukturę wewnątrz kontrolera. gdy masz tablicę $ scope.rows, możesz wygenerować tabelę za pomocą pojedynczego, prostego powtórzenia ng. Żadnego hakowania, żadnej nieelegancji, po prostu sposób, w jaki został zaprojektowany do działania.

Dyrektywom Angulars nie brakuje funkcjonalności. Po prostu zmuszają cię do napisania prawidłowego html. Mój kolega miał podobny problem, cytując @bmoeskau na poparcie krytyki dotyczącej funkcji tworzenia szablonów / renderowania kątów. Przyglądając się dokładnemu problemowi, okazało się, że po prostu chciał wygenerować tag otwarty, potem tag zamknięty w innym miejscu, itd., Tak jak za dawnych dobrych czasów, kiedy łączyliśmy nasz kod HTML ze stringów… prawda? Nie.

jeśli chodzi o spłaszczenie struktury w 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']

zadziała to dla każdej struktury podobnej do drzewa, która zawiera elementy podrzędne. Jeśli chcesz mieć cały element zamiast właściwości, możesz jeszcze bardziej skrócić funkcję spłaszczania.

mam nadzieję, że to pomoże.

Ar Es
źródło
Niezłe. Chociaż nie pomogło mi to bezpośrednio, otworzyło mi umysł na inne rozwiązanie. Wielkie dzięki!
Marian Zburlea
0

za rozwiązanie, które naprawdę działa

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>

dodaj dyrektywę 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]);
}());

dla krótkiego wyjaśnienia,

ng-repeat wiąże się z plikiem <remove> elementem i zapętla się tak, jak powinno, a ponieważ użyliśmy ng-repeat-start / ng-repeat-end, zapętla blok html, a nie tylko element.

następnie niestandardowa dyrektywa remove umieszcza elementy <remove>początkowe i końcowe za pomocą<!--removed element-->

Clint
źródło
-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>

Myślę, że to ważne :)

Renan Tomal Fernandes
źródło
Chociaż to działa, będzie działać tylko wtedy, gdy tablica ma trzy elementy.
btford,
Po prostu upewnij się, że tablica ma 3 elementy, nawet jeśli są to puste tablice (ng-repeat z pustą tablicą po prostu niczego nie renderuj).
Renan Tomal Fernandes,
7
Chodziło mi o to, że OP prawdopodobnie chce rozwiązania, które działa dla zmiennej liczby elementów w tablicy. Zakładam, że zakodowanie „muszą być trzy elementy w tej tablicy” w szablonie byłoby kiepskim rozwiązaniem.
btford,
W ostatnim komentarzu do swojego pytania mówi, że elementy nie będą miały takiej samej struktury, więc zakodowanie każdej struktury jest nieuniknione.
Renan Tomal Fernandes,