AngularJS ng-repeat ohne HTML-Element

91

Ich verwende derzeit diesen Code, um eine Liste zu rendern:

<ul ng-cloak>
    <div ng-repeat="n in list">
        <li><a href="{{ n[1] }}">{{ n[0] }}</a></li>
        <li class="divider"></i>
    </div>
    <li>Additional item</li> 
</ul>

Das <div>Element verursacht jedoch in einigen Browsern einige sehr geringfügige Renderfehler. Ich würde gerne wissen, ob es eine Möglichkeit gibt, die ng-Wiederholung ohne den div-Container oder eine alternative Methode durchzuführen, um den gleichen Effekt zu erzielen.

nehz
quelle
Über welche Rendering-Probleme sprechen Sie?
Ja͢ck
Der letzte Li-Teiler scheint etwas dicker zu sein als sein gestapelter (Chrome Win7). Dies könnte ein CSS-Problem sein, ich würde jedoch gerne wissen, ob dies im Winkel behoben werden kann. Zum Vergleich erlaubt knockoutJS containerlose Bindungen mit <! - ->.
Nehz
Ich sehe, ich benutze phptal auf der Serverseite und sie haben ein tal: block-Tag speziell für diesen Zweck :) Vermutung, dass DOM nicht immer eine neue Art von Tag akzeptiert, die mit
Angular
Könnten Sie auch das HTML aktualisieren, um zu reflektieren, wie Sie die htmlAppendDirektive verwenden?
Nick
Fertig ... habe das vor
einiger

Antworten:

104

Wie Andy Joslin sagte, arbeiteten sie an kommentarbasierten ng-Wiederholungen, aber anscheinend gab es zu viele Browserprobleme . Glücklicherweise bietet AngularJS 1.2 eine integrierte Unterstützung für das Wiederholen, ohne untergeordnete Elemente mit den neuen Anweisungen ng-repeat-startund hinzuzufügen ng-repeat-end.

Hier ist ein kleines Beispiel für das Hinzufügen einer Bootstrap-Paginierung :

<ul class="pagination">
  <li>
    <a href="#">&laquo;</a>
  </li>
  <li ng-repeat-start="page in [1,2,3,4,5,6]"><a href="#">{{page}}</a></li>
  <li ng-repeat-end class="divider"></li>
  <li>
    <a href="#">&raquo;</a>
  </li>
</ul>

Ein vollständiges Arbeitsbeispiel finden Sie hier .

John Lindquist hat auch ein Video-Tutorial dazu auf seiner ausgezeichneten Egghead.io-Seite .

jmagnusson
quelle
3
Benötigt dies nicht einen Elementdruck?
Hitautodestruct
2
Ich bin ernsthaft verwirrt darüber, was dies über <li ng-repeat = "page in [1,2,3,4,5,6]"> <a href="#"> {{page}} </ a bewirkt > </ li> -edit: Vergiss nicht, ich glaube ich
verstehe
6
Ich verstehe, dass Sie nur versuchen zu demonstrieren, ng-repeat-start/endaber dies ist ein verwirrendes Beispiel und der falsche Anwendungsfall dafür. Hier ng-repeatsollte ein Standard verwendet werden, da Ihr Code lifür jedes Element ein zusätzliches Leerzeichen darstellt. Sie sollten das wahrscheinlich in Ihrem Beitrag erwähnen.
GFoley83
2
@ GFoley83 du bist richtig. Aber er scheint dieses zusätzliche Element für jede Iteration zu wollen, etwas, ohne das es nicht möglich ist ng-repeat-start. Ich werde die dividerHTML-Klasse zu meiner Antwort hinzufügen , um klarer zu sein.
jmagnusson
1
ng-repeat-startund ng-repeat-endsind ein wenig verwirrend. Die Angular-Dokumentation hilft: https://docs.angularjs.org/api/ng/directive/ngRepeat#special-repeat-start-and-end-points
perilandmishap
30

KnockoutJS containerlose Bindungssyntax

Bitte nehmen Sie sich eine Sekunde Zeit: KnockoutJS bietet eine äußerst bequeme Möglichkeit, eine containerlose Bindungssyntax für die foreachBindung zu verwenden, wie in Anmerkung 4 der foreachBindungsdokumentation erläutert . http://knockoutjs.com/documentation/foreach-binding.html

Wie das Beispiel für die Knockout-Dokumentation zeigt, können Sie Ihre Bindung wie folgt in KnockoutJS schreiben:

<ul>
    <li class="header">Header item</li>
    <!-- ko foreach: myItems -->
        <li>Item <span data-bind="text: $data"></span></li>
    <!-- /ko -->
</ul>

Ich finde es eher bedauerlich, dass AngularJS diese Art von Syntax nicht anbietet.


Winkel ng-repeat-startundng-repeat-end

Bei der AngularJS-Methode zur Lösung von ng-repeatProblemen sind die Beispiele, auf die ich stoße, vom Typ jmagnusson, der in seiner (hilfreichen) Antwort veröffentlicht wurde.

<li ng-repeat-start="page in [1,2,3,4,5]"><a href="#">{{page}}</a></li>
<li ng-repeat-end></li>

Mein ursprünglicher Gedanke beim Sehen dieser Syntax ist: wirklich? Warum erzwingt Angular all dieses zusätzliche Markup, mit dem ich nichts zu tun haben möchte und das in Knockout so viel einfacher ist? Aber dann begann mich der Kommentar von hitautodestruct in jmagnussons Antwort zu fragen: Was wird mit ng-repeat-start und ng-repeat-end auf separaten Tags generiert?


Eine sauberere Art zu verwenden ng-repeat-startundng-repeat-end

Bei der Untersuchung der Behauptung von hitautodestruct ist das Hinzufügen ng-repeat-endzu einem separaten Tag genau das, was ich in den meisten Fällen nicht tun möchte, da es völlig nutzlose Elemente erzeugt: in diesem Fall <li>Elemente, in denen sich nichts befindet. Die paginierte Liste von Bootstrap 3 formatiert die Listenelemente so, dass es so aussieht, als hätten Sie keine überflüssigen Elemente generiert. Wenn Sie jedoch das generierte HTML überprüfen, sind sie vorhanden.

Glücklicherweise müssen Sie nicht viel tun, um eine sauberere Lösung und eine kürzere Menge an HTML zu erhalten: Setzen Sie die ng-repeat-endDeklaration einfach auf dasselbe HTML-Tag, das das enthältng-repeat-start .

<ul class="pagination">
  <li>
    <a href="#">&laquo;</a>
  </li>    
  <li ng-repeat-start="page in [1,2,3,4,5]" ng-repeat-end><a href="#"></a></li>
  <li>
    <a href="#">&raquo;</a>
  </li>
</ul>

Dies bietet 3 Vorteile:

  1. weniger HTML-Tags zu schreiben
  2. nutzlose, leere Tags werden von Angular nicht generiert
  3. Wenn das zu wiederholende Array leer ist, wird das Tag mit ng-repeatnicht generiert, was Ihnen den gleichen Vorteil bietet, den Ihnen die containerlose Bindung von Knockout in dieser Hinsicht bietet

Aber es gibt immer noch einen saubereren Weg

Nachdem Sie die Kommentare in github zu diesem Problem für Angular unter https://github.com/angular/angular.js/issues/1891 weiter gelesen haben , müssen
Sie nicht dieselben Vorteile verwenden ng-repeat-startund ng-repeat-enderzielen. Stattdessen können wir, wenn wir noch einmal jmagnussons Beispiel gabeln, einfach gehen:

<ul class="pagination">
  <li>
    <a href="#">&laquo;</a>
  </li>
  <li ng-repeat="page in [1,2,3,4,5,6]"><a href="#">{{page}}</a></li>
  <li>
    <a href="#">&raquo;</a>
  </li>
</ul>

Also wann ng-repeat-startund verwenden ng-repeat-end? Gemäß der eckigen Dokumentation zu

... eine Reihe von Elementen anstelle von nur einem übergeordneten Element wiederholen ...

Genug geredet, zeigen Sie einige Beispiele!

Meinetwegen; Dieser JSBIN führt fünf Beispiele durch, was passiert, wenn Sie es tun und wenn Sie es nicht ng-repeat-endfür dasselbe Tag verwenden.

http://jsbin.com/eXaPibI/1/

mg1075
quelle
11
Sie scheinen den Punkt der Frage falsch verstanden zu haben. Ziel ist es, zwei oder mehr Listenelemente gleichzeitig zu wiederholen, und weder Ihr Beispiel noch die von Ihnen verlinkte Seite bieten eine Möglichkeit, dies zu erreichen. Wie Sie selbst bemerkt haben, ist das Anhängen ng-repeat-startund Anhängen ng-repeat-endan dasselbe Element völlig nutzlos, wenn Sie die Standardeinstellung von Angular verwenden ng-repeatund damit fertig sind.
Asad Saeeduddin
@Asad - "der Punkt der Frage" wird vom OP nicht klar angegeben. Haben Sie auch die gerenderte DOM-Ausgabe der akzeptierten Antwort überprüft? Ich kann mir nicht vorstellen, dass jemand diese Art von Ausgabe möchte, zumindest nicht für eine Bootstrap-Paginierungsliste.
mg1075
1
@ mg1075 Ja, die Ausgabe der akzeptierten Antwort ist nicht akzeptabel, weshalb ich hier ganz nach unten gescrollt habe. Der Punkt der Frage ist sehr klar: Finden Sie einen Weg, um eine Anwendung zu ng-repeatfinden oder eine Alternative zur Verwendung von OP zu finden ng-repeat, die das divArtefakt im HTML beseitigt . Ihre Antwort tut dies nicht, da es keine Möglichkeit gibt, sie für die beiden liElemente in der Frage zu verwenden. Wenn ich hier falsch liege, geben Sie bitte ein Beispiel-Markup an, das den OP-Code anpasst, um das divElement zu entfernen .
Asad Saeeduddin
@Asad - Der Punkt der Frage wäre klarer gewesen, wenn das OP ein fertiges Beispiel für die von ihm beabsichtigte Ausgabe geliefert hätte. Die Tatsache, dass er die "akzeptierte" Antwort akzeptierte, bei der die akzeptierte Antwort eine Bootstrap-Paginierung mit im Wesentlichen falschem Markup verwendet, trübte das Problem nur weiter. Beispiel 2 im jsbin löst, wenn dies wirklich beabsichtigt ist, das Problem des OP so, wie es die akzeptierte Antwort tut, aber niemand sollte diese Art von Markup für die Bootstrap-Paginierung verwenden. jsbin.com/eXaPibI/1
mg1075
1
Eigentlich habe ich mir die erste Antwort noch einmal angesehen und das generierte Markup ist genau richtig für den Anwendungsfall des OP, obwohl es meinen Anforderungen nicht entspricht. Das OP möchte, dass ein Teilerelement generiert wird. Dies liist das Extra, das liSie in Ihrem jsbin sehen.
Asad Saeeduddin
6

ngRepeat reicht möglicherweise nicht aus, Sie können dies jedoch mit einer benutzerdefinierten Direktive kombinieren. Sie können die Aufgabe des Hinzufügens von Teilerelementen zum Code delegieren, wenn Ihnen ein wenig jQuery nichts ausmacht.

 <li ng-repeat="item in coll" so-add-divide="your exp here"></li>

Solch eine einfache Direktive benötigt nicht wirklich einen Attributwert, bietet Ihnen jedoch viele Möglichkeiten, wie das bedingte Hinzufügen eines Teilers nach Index, Länge usw. oder etwas völlig anderem.

Orcun
quelle
2
cool Ich habe eine benutzerdefinierte Direktive hinzugefügt und es funktioniert. Diese Videoanleitung half: youtube.com/watch?v=A6wq16Ow5Ec
nehz
3

Ich hatte kürzlich das gleiche Problem, da ich eine beliebige Sammlung von Bereichen und Bildern wiederholen musste - ein Element um sie herum war keine Option - es gibt eine einfache Lösung, erstellen Sie jedoch eine "Null" -Richtlinie:

app.directive("diNull", function() {
        return {
            restrict: "E",
            replace: true,
            template: ""
        };
    });

Sie können dann eine Wiederholung für dieses Element verwenden, wobei element.url auf die Vorlage für dieses Element verweist:

<di-null ng-repeat="element in elements" ng-include="element.url" ></di-null>

Dadurch werden beliebig viele verschiedene Vorlagen ohne Container wiederholt

Hinweis: hmm, ich hätte schwören können, dass dies das di-null-Element beim Rendern entfernt hat, aber wenn ich es erneut überprüfe, werden meine Layoutprobleme nicht gelöst ... Kurioser und Kurioser ...

Raphael Huerzeler
quelle
replaceist seit 2.0 veraltet.
Demalexx
Ich habe das oben versucht, aber Vorlage: "" bewirkt, dass nichts geändert wird. Ich habe den Linker von jsfiddle.net/markcoleman/fMP9s/1 verwendet und geändert: link: function (scope, element, attrs) {element [0] .outerHTML = ''; $ compile (element.contents ()) (scope); }
Lauri Lubi
1

Es gibt eine Einschränkung der Kommentaranweisung, aber ngRepeat unterstützt sie nicht (da ein Element zum Wiederholen benötigt wird).

Ich glaube, ich habe gesehen, wie das eckige Team sagte, sie würden an Kommentaren und Wiederholungen arbeiten, aber ich bin mir nicht sicher. Sie sollten ein Problem dafür im Repo öffnen. http://github.com/angular/angular.js

Andrew Joslin
quelle
Kommentar von 2012. Ist das noch der Fall? Versucht, in ihrer Dokumentation zu suchen, und konnte keine Referenz finden.
Zack S
1

Es gibt keine magische Methode für Angular, um dies zu tun. In Ihrem Fall können Sie dies tun, um gültiges HTML zu erhalten, wenn Sie Bootstrap verwenden. Dann erhalten Sie den gleichen Effekt wie beim Hinzufügen des li.dividers

Erstellen Sie eine Klasse:

span.divider {
 display: block;
}

Ändern Sie nun Ihren Code wie folgt:

<ul ng-cloak>
 <li div ng-repeat="n in list">
    <a href="{{ n[1] }}">{{ n[0] }}</a>
    <span class="divider"></span>
 </li>
 <li>Additional item</li>
</ul>
Eduardo in Norwegen
quelle
1

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

Clint
quelle
Das ist interessant. In meinen replaceWith(...)Testergebnissen in einem Textknoten jedoch kein Kommentar. Ich habe nur element.remove()Werke gefunden.
Artfulrobot