AngularJS ng-repeat bez elementu HTML

91

Obecnie używam tego fragmentu kodu do renderowania listy:

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

Jednak <div>element powoduje bardzo drobne błędy renderowania w niektórych przeglądarkach. Chciałbym wiedzieć, czy istnieje sposób na wykonanie powtórzenia ng bez kontenera div lub jakaś alternatywna metoda, aby osiągnąć ten sam efekt.

nehz
źródło
O jakich problemach z renderowaniem mówisz?
Ja͢ck
ostatni rozdzielacz li wydaje się nieco grubszy niż jego stacked (chrome win7). Może to być problem z CSS, jednak chciałbym wiedzieć, czy można to naprawić w trybie kątowym. Dla porównania knockoutJS pozwala na powiązania bez kontenera przy użyciu <! - ->.
nehz
Widzę, używam phptal po stronie serwera i mają tag tal: block specjalnie do tego celu :) zgadnij, że DOM nie zawsze akceptuje nowy rodzaj tagu, który jest trudniejszy z
angularem
Czy mógłbyś również zaktualizować kod HTML, aby odzwierciedlić sposób korzystania z htmlAppenddyrektywy?
Nick
Zrobione ... zrobiłem to jakiś czas temu, ale pamiętam, że działało
nehz

Odpowiedzi:

104

Jak powiedział Andy Joslin, pracowali nad komentarzami opartymi na powtórzeniach ng, ale najwyraźniej było zbyt wiele problemów z przeglądarką . Na szczęście AngularJS 1.2 dodaje wbudowaną obsługę powtarzania bez dodawania elementów potomnych z nowymi dyrektywami ng-repeat-starti ng-repeat-end.

Oto mały przykład dodawania paginacji Bootstrap :

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

Pełny przykład roboczy można znaleźć tutaj .

John Lindquist ma również samouczek wideo na swojej doskonałej stronie egghead.io .

jmagnusson
źródło
3
Czy to nie wymaga nadruku elementu?
hitautodestruct
2
Jestem poważnie zdezorientowany, co to daje po prostu <li ng-repeat = "strona w [1,2,3,4,5,6]"> <a href="#"> {{page}} </ a > </li> -edit: nieważne, myślę, że to rozumiem
Shadaez
6
Rozumiem, że próbujesz tylko zademonstrować, ng-repeat-start/endale to jest mylący przykład i niewłaściwy przypadek użycia. ng-repeatNależy tutaj użyć standardu , ponieważ kod renderuje dodatkowe puste miejsce lidla każdego elementu. Powinieneś prawdopodobnie wspomnieć o tym w swoim poście.
GFoley83
2
@ GFoley83 masz rację. Ale wydaje się, że chce tego dodatkowego elementu dla każdej iteracji, bez czego nie jest możliwe ng-repeat-start. Dodam dividerklasę html do mojej odpowiedzi, aby była bardziej przejrzysta.
jmagnusson
1
ng-repeat-starti ng-repeat-endsą trochę zagmatwane.
Pomocna jest
30

Składnia wiązania bez kontenera KnockoutJS

Proszę o chwilę cierpliwości: KnockoutJS oferuje bardzo wygodną opcję użycia bezkontenerowej składni wiązania do foreachpowiązania, jak omówiono w uwadze 4 foreachdokumentacji powiązań. http://knockoutjs.com/documentation/foreach-binding.html

Jak pokazuje przykład dokumentacji Knockout, możesz napisać swoje powiązanie w KnockoutJS w następujący sposób:

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

Myślę, że to raczej niefortunne, że AngularJS nie oferuje tego typu składni.


Angular ng-repeat-starting-repeat-end

W sposobie rozwiązywania ng-repeatproblemów przez AngularJS , próbki, które napotykam, są typu jmagnusson zamieszczonego w jego (pomocnej) odpowiedzi.

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

Moja pierwotna myśl po zobaczeniu tej składni brzmi: naprawdę? Dlaczego Angular wymusza te wszystkie dodatkowe znaczniki, z którymi nie chcę mieć nic wspólnego, a to jest o wiele łatwiejsze w Knockout? Ale potem komentarz hitautodestruct w odpowiedzi jmagnusson zaczął mnie zastanawiać: co jest generowane przez ng-repeat-start i ng-repeat-end w oddzielnych tagach?


Czystszy sposób używania ng-repeat-starting-repeat-end

Po zbadaniu twierdzenia hitautodestruct dodanie ng-repeat-enddo oddzielnego tagu jest dokładnie tym, czego nie chciałbym robić w większości przypadków, ponieważ generuje całkowicie bezużyteczne elementy: w tym przypadku <li>elementy, w których nic nie ma. Lista podzielona na strony w Bootstrap 3 stylizuje elementy listy w taki sposób, że wygląda na to, że nie wygenerowałeś żadnych zbędnych elementów, ale kiedy sprawdzasz wygenerowany kod HTML, są one tam.

Na szczęście nie trzeba wiele robić, aby mieć czystsze rozwiązanie i mniejszą ilość html: wystarczy umieścić ng-repeat-enddeklarację na tym samym tagu html, który ma rozszerzenieng-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>

Daje to 3 korzyści:

  1. mniej tagów HTML do napisania
  2. bezużyteczne, puste tagi nie są generowane przez Angular
  3. gdy tablica do powtórzenia jest pusta, tag z ng-repeatnie zostanie wygenerowany, co daje taką samą korzyść, jaką daje w tym przypadku bezkontenerowe wiązanie Knockouta

Ale jest jeszcze czystszy sposób

Po dokładniejszym przejrzeniu komentarzy na githubie dotyczących tego problemu dla Angular, https://github.com/angular/angular.js/issues/1891 ,
nie musisz używać ng-repeat-starti ng-repeat-endosiągać tych samych korzyści. Zamiast tego, ponownie rozwidlając przykład jmagnusson, możemy po prostu:

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

Kiedy więc używać ng-repeat-starti ng-repeat-end? Zgodnie z dokumentacją kątową , do

... powtórz serię elementów zamiast tylko jednego elementu nadrzędnego ...

Dość gadania, pokaż kilka przykładów!

Słusznie; ten plik jsbin omawia pięć przykładów tego, co się dzieje, gdy to robisz, a kiedy nie używasz ng-repeat-endtego samego tagu.

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

mg1075
źródło
11
Wydaje się, że źle zrozumiałeś sens pytania. Celem jest jednoczesne powtórzenie dwóch lub więcej elementów listy, a ani przykład, ani strona, do której prowadzi łącze, nie stanowią sposobu na osiągnięcie tego celu. Jak sam zauważyłeś, dołączanie ng-repeat-starti ng-repeat-enddo tego samego elementu jest całkowicie bezużyteczne, gdy możesz użyć wartości domyślnej angular ng-repeati skończyć z tym.
Asad Saeeduddin
@Asad - PO nie jest jasno sformułowane w PO. Czy sprawdziłeś też wyrenderowane wyjście DOM zaakceptowanej odpowiedzi? Nie wyobrażam sobie nikogo, kto chciałby tego rodzaju wyników, przynajmniej na pewno nie w przypadku listy stronicowania bootstrap.
mg1075
1
@ mg1075 Tak, wynik zaakceptowanej odpowiedzi jest nie do przyjęcia, dlatego przewinęłam całą drogę w tym miejscu. Pytanie jest bardzo jasne: znajdź sposób na zastosowanie ng-repeatlub znalezienie alternatywy dla użycia OP, ng-repeatktóra wyeliminuje divartefakt w HTML. Twoja odpowiedź tego nie robi, ponieważ nie ma sposobu, aby użyć jej dla dwóch lielementów w pytaniu. Jeśli się mylę, podaj przykładowe znaczniki, które dostosowują kod OP w celu wyeliminowania divelementu.
Asad Saeeduddin
@Asad - Punkt pytania byłby jaśniejszy, gdyby PO przedstawił gotowy przykład zamierzonego produktu. Fakt, że zaakceptował odpowiedź „zaakceptowaną”, w której przyjęta odpowiedź wykorzystuje paginację typu bootstrap z zasadniczo niewłaściwym znacznikiem, tylko pogmatwał sprawę. Przykład 2 w jsbin, jeśli jest to naprawdę zamierzone, rozwiązuje problem PO w sposób, w jaki działa zaakceptowana odpowiedź, ale nikt nie powinien używać tego typu znaczników do paginacji bootstrap. jsbin.com/eXaPibI/1
mg1075
1
Właściwie tylko spojrzałem na pierwszą odpowiedź ponownie i wygenerowany znacznik jest dokładnie odpowiedni dla przypadku użycia OP, nawet jeśli nie spełnia moich wymagań. OP chce, aby lizostał wygenerowany element lidzielący , czyli dodatkowy, który widzisz w swoim jsbin.
Asad Saeeduddin
6

ngRepeat może nie wystarczyć, ale można to połączyć z niestandardową dyrektywą. Możesz delegować zadanie dodawania elementów rozdzielających do kodu, jeśli nie masz nic przeciwko odrobinie jQuery.

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

Taka prosta dyrektywa tak naprawdę nie potrzebuje wartości atrybutu, ale może dać ci wiele możliwości, takich jak warunkowe dodanie dzielnika według indeksu, długości itp. Lub coś zupełnie innego.

orcun
źródło
2
fajne Dodałem dyrektywę niestandardową i działa. Ten przewodnik wideo pomógł: youtube.com/watch?v=A6wq16Ow5Ec
nehz
3

Niedawno miałem ten sam problem polegający na tym, że musiałem powtórzyć dowolną kolekcję przęseł i obrazów - posiadanie elementu wokół nich nie wchodziło w grę - jest jednak proste rozwiązanie, jednak stwórz dyrektywę „null”:

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

Następnie możesz użyć powtórzenia dla tego elementu, gdzie element.url wskazuje na szablon dla tego elementu:

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

Spowoduje to powtórzenie dowolnej liczby różnych szablonów bez żadnego kontenera wokół nich

Uwaga: hmm, mógłbym przysiąc, że to usunęło element di-null podczas renderowania, ale ponowne sprawdzenie tego nie ... nadal rozwiązało moje problemy z układem ... ciekawość i ciekawość ...

Raphael Huerzeler
źródło
replacejest przestarzałe od wersji 2.0.
demalexx
Wypróbowałem powyższe, ale szablon: „” powoduje, że nic się nie zmienia. Użyłem linkera z jsfiddle.net/markcoleman/fMP9s/1 i zmodyfikowałem go: link: function (scope, element, attrs) {element [0] .outerHTML = ''; $ compile (element.contents ()) (zakres); }
Lauri Lubi
1

Istnieje ograniczenie dyrektywy komentarza, ale ngRepeat go nie obsługuje (ponieważ potrzebuje elementu do powtórzenia).

Myślę, że widziałem, jak zespół Angular powiedział, że będzie pracował nad komentarzami ng-powtórzeń, ale nie jestem pewien. Powinieneś otworzyć problem w repozytorium. http://github.com/angular/angular.js

Andrew Joslin
źródło
Komentarz z 2012 r. Czy nadal tak jest? Próbowałem przeszukać ich dokumentację i nie mogłem znaleźć żadnego odniesienia.
Zack S
1

Nie ma magicznego sposobu Angulara, aby to zrobić, w twoim przypadku możesz to zrobić, aby uzyskać prawidłowy HTML, jeśli używasz Bootstrap. Wtedy uzyskasz taki sam efekt, jak dodanie rozdzielacza li

Utwórz zajęcia:

span.divider {
 display: block;
}

Teraz zmień swój kod na ten:

<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 w Norwegii
źródło
1

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

na krótkie wyjaśnienie,

ng-repeat wiąże się z <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
To jest interesujące. Jednak moje testy replaceWith(...)pokazują, że węzeł tekstowy nie jest komentarzem. Odkryłem, że używam tylko element.remove()prac.
artfulrobot