Verwendung von ng-repeat ohne HTML-Element


142

Ich muss ng-repeat(in AngularJS) verwenden, um alle Elemente in einem Array aufzulisten.

Die Komplikation besteht darin, dass jedes Element des Arrays in eine, zwei oder drei Zeilen einer Tabelle umgewandelt wird.

Ich kann kein gültiges HTML erstellen, wenn ng-repeates für ein Element verwendet wird, da zwischen <tbody>und kein Typ eines sich wiederholenden Elements zulässig ist <tr>.

Wenn ich zum Beispiel ng-repeat on verwenden <span>würde, würde ich Folgendes erhalten:

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

Welches ist ungültig HTML.

Aber was ich generiert werden muss, ist:

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

Dabei wurde die erste Zeile vom ersten Array-Element generiert, die nächsten drei vom zweiten und die fünfte und sechste vom letzten Array-Element.

Wie kann ich ng-repeat so verwenden, dass das HTML-Element, an das es gebunden ist, beim Rendern "verschwindet"?

Oder gibt es dafür eine andere Lösung?


Klarstellung: Die generierte Struktur sollte wie folgt aussehen. Jedes Array-Element kann zwischen 1-3 Zeilen der Tabelle generieren. Die Antwort sollte idealerweise 0-n Zeilen pro Array-Element unterstützen.

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

Vielleicht sollten Sie "replace: true" verwenden? Siehe dies: stackoverflow.com/questions/11426114/…

Warum können Sie ng-repeat nicht auf dem tr selbst verwenden?

2
@Tommy, weil "jedes Element des Arrays entweder in eine, zwei oder drei Zeilen einer Tabelle umgewandelt wird". Wenn ich ng-repeat für verwenden trwürde, würde ich, soweit ich weiß, eine Zeile pro Array-Element erhalten.
Fadedbee

1
OK, ich verstehe. Können Sie das Modell nicht einfach abflachen, bevor Sie es im Repeater verwenden?

@ Tommy, nein. Die 1-3 trs, die von einem Array-Element erzeugt werden, haben nicht die gleiche Struktur.
Fadedbee

Antworten:


66

Update: Wenn Sie Angular 1.2+ verwenden, verwenden Sie ng-repeat-start . Siehe @ jmagnussons Antwort.

Ansonsten, wie wäre es, wenn Sie die ng-Wiederholung auf tbody setzen? (AFAIK, es ist in Ordnung, mehrere <tbody> s in einer einzigen Tabelle zu haben.)

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

62
Obwohl dies technisch funktionieren könnte, ist es sehr enttäuschend, dass die Antwort für diesen häufigen Anwendungsfall darin besteht, dass Sie willkürliches (ansonsten unnötiges) Markup einfügen müssen. Ich habe das gleiche Problem (wiederholte Gruppen von Zeilen - ein Header-TR mit einem oder mehreren untergeordneten TRs, wiederholt als Gruppe). Trivial mit anderen Template-Engines, bestenfalls mit Angular hacky, wie es scheint.
Brian Moeskau

1
Einverstanden. Ich versuche, die DT + DD-Elemente zu wiederholen. Es gibt keine Möglichkeit, dies zu tun, ohne ein ungültiges Wrapping-Element hinzuzufügen
David Lin

2
@DavidLin, in Angular v1.2 (wann immer es herauskommt) können Sie mehrere Elemente wiederholen, z. B. dt und dd: youtube.com/watch?v=W13qDdJDHp8&t=17m28s
Mark Rajcok

Dies wird in KnockoutJS mithilfe der containerlosen Kontrollflusssyntax elegant gehandhabt: knockoutjs.com/documentation/foreach-binding.html . Ich hoffe, Angular bald etwas Ähnliches tun zu sehen.
Cory House

3
Diese Antwort ist veraltet, verwenden Sie stattdessen ng-repeat-start
iwein

73

Ab AngularJS 1.2 gibt es eine Direktive namens ng-repeat-start, die genau das tut, wonach Sie fragen. In meiner Antwort in dieser Frage finden Sie eine Beschreibung der Verwendung.


Angular hat aufgeholt, dies ist jetzt die richtige Lösung.
Iwein

33

Wenn Sie ng> 1.2 verwenden, finden Sie hier ein Beispiel für die Verwendung, ng-repeat-start/endohne unnötige Tags zu generieren:

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

Der wichtige Punkt: für Tags, die für verwendet ng-repeat-startund ng-repeat-endfestgelegt werden ng-if="0", damit sie nicht in die Seite eingefügt werden. Auf diese Weise wird der innere Inhalt genauso behandelt wie in knockoutjs (mit Befehlen in <!--...-->), und es entsteht kein Müll.


Nach dem Setzen von ng-if = "0" wird der Fehler ausgegeben, der besagt, dass das passende ng-repeat-end nicht gefunden werden kann.
Vikky MCTS

19

Möglicherweise möchten Sie die Daten in Ihrem Controller reduzieren:

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

Und dann im HTML:

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

1
Nein, ist es nicht. Das Aufrufen einer Funktion innerhalb eines ngRepeat-Ausdrucks wird absolut nicht empfohlen
Nik

In meinem Fall habe ich brauche einen Separator für jedes Element in dem der Index = 0 und hinzuzufügen! ng-repeat-start, ng-repeat-endBrechen mein Design, so dass die Lösung eine zusätzliche Variable Hinzufügen dieser Separator Objekt vor jedem Element und Iterierte das neue var zu schaffen war: <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

Das Obige ist richtig, aber für eine allgemeinere Antwort reicht es nicht aus. Ich musste ng-repeat verschachteln, aber auf derselben HTML-Ebene bleiben, was bedeutet, dass ich die Elemente in dasselbe übergeordnete Element schreibe. Das Tags-Array enthält Tags, die auch ein Tags-Array haben. Es ist eigentlich ein Baum.

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

Also hier ist, was ich schließlich getan habe.

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

Beachten Sie das ng-if = "false", das die Start- und End-Divs verbirgt.

Es sollte gedruckt werden

name1, name1_1, name1_2, name2, name2_1, name2_2,


1

Ich möchte nur einen Kommentar abgeben, aber mein Ruf fehlt immer noch. Also füge ich eine weitere Lösung hinzu, die das Problem ebenfalls löst. Ich möchte die Aussage von @bmoeskau wirklich widerlegen, dass die Lösung dieses Problems eine "bestenfalls hackige" Lösung erfordert, und da dies kürzlich in einer Diskussion auftauchte, obwohl dieser Beitrag 2 Jahre alt ist, möchte ich meine hinzufügen besitzen zwei Cent:

Wie @btford hervorgehoben hat, scheinen Sie zu versuchen, eine rekursive Struktur in eine Liste umzuwandeln. Daher sollten Sie diese Struktur zuerst in eine Liste reduzieren. Seine Lösung macht das, aber es gibt eine Meinung, dass das Aufrufen der Funktion in der Vorlage unelegant ist. Wenn das wahr ist (ehrlich gesagt, ich weiß es nicht), würde das nicht nur die Ausführung der Funktion in der Steuerung und nicht der Direktive erfordern?

In beiden Fällen benötigt Ihr HTML eine Liste, sodass der Bereich, in dem es gerendert wird, über diese Liste verfügen sollte. Sie müssen lediglich die Struktur in Ihrem Controller reduzieren. Sobald Sie ein $ scope.rows-Array haben, können Sie die Tabelle mit einer einzigen einfachen ng-Wiederholung generieren. Kein Hacken, keine Uneleganz, einfach so, wie es funktionieren soll.

Angulars-Direktiven mangelt es nicht an Funktionalität. Sie zwingen Sie einfach, gültiges HTML zu schreiben. Ein Kollege von mir hatte ein ähnliches Problem und zitierte @bmoeskau zur Unterstützung der Kritik an Vorlagen- / Rendering-Funktionen für Winkel. Als er sich das genaue Problem ansah, stellte sich heraus, dass er einfach ein Open-Tag generieren wollte, dann ein Close-Tag woanders usw. Genau wie in den guten alten Zeiten, als wir unser HTML aus Strings zusammenlegten. Richtig? Nein.

Hier ist eine andere Lösung, um die Struktur in eine Liste zu reduzieren:

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

Dies funktioniert für jede baumartige Struktur mit Unterelementen. Wenn Sie das gesamte Element anstelle einer Eigenschaft möchten, können Sie die Abflachungsfunktion noch weiter verkürzen.

hoffentlich hilft das.


Schön. Es hat mir zwar nicht direkt geholfen, aber es hat mich für eine andere Lösung geöffnet. Vielen Dank!
Marian Zburlea

0

für eine Lösung, die wirklich funktioniert

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>

Fügen Sie eine angle.js-Direktive hinzu

//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]);
}());

für eine kurze Erklärung,

ng-repeat bindet sich an das <remove>Element und schleift es wie es sollte, und da wir ng-repeat-start / ng-repeat-end verwendet haben, schleift es einen HTML-Block, nicht nur ein Element.

Anschließend platziert die benutzerdefinierte Direktive zum Entfernen die <remove>Start- und Endelemente mit<!--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>

Ich denke das ist gültig :)


Während dies funktioniert, funktioniert es nur, wenn das Array drei Elemente enthält.
Btford

Stellen Sie einfach sicher, dass das Array 3 Elemente enthält, auch wenn es sich um leere Arrays handelt (ng-repeat mit einem leeren Array rendert einfach nichts).
Renan Tomal Fernandes

7
Mein Punkt war, dass das OP wahrscheinlich eine Lösung wünscht, die für eine variable Anzahl von Elementen im Array funktioniert. Ich gehe davon aus, dass die Hardcodierung "Es müssen drei Elemente in diesem Array sein" in die Vorlage eine schlechte Lösung wäre.
Btford

Im letzten Kommentar zu seiner Frage sagt er, dass die Elemente nicht dieselbe Struktur haben werden, so dass eine Hardcodierung jeder Struktur unvermeidlich ist.
Renan Tomal Fernandes
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.