Kątowy ng-repeat dodaje wiersz bootstrap co 3 lub 4 cols

111

Szukam odpowiedniego wzorca do wstrzykiwania klasy wiersza ładowania początkowego co każde 3 kolumny. Potrzebuję tego, ponieważ kolumny nie mają ustalonej wysokości (i nie chcę jej naprawiać), więc psuje mój projekt!

Oto mój kod:

<div ng-repeat="product in products">
    <div ng-if="$index % 3 == 0" class="row">
        <div class="col-sm-4" >
            ...
        </div>
    </div>
</div>

Ale wyświetla tylko jeden produkt w każdym wierszu. Jako wynik końcowy chcę:

<div class="row">
    <div class="col-sm4"> ... </div>
    <div class="col-sm4"> ... </div>
    <div class="col-sm4"> ... </div>
</div>
<div class="row">
    <div class="col-sm4"> ... </div>
    <div class="col-sm4"> ... </div>
    <div class="col-sm4"> ... </div>
</div>

Czy mogę to osiągnąć za pomocą samego wzoru powtarzania ng (bez dyrektywy lub kontrolera)? W docs wprowadzają NG-repeat-start i NG-repeat-end, ale nie mogę dowiedzieć się, jak wykorzystać to właśnie ten przypadek użycia! Czuję, że jest to coś, czego często używamy w tworzeniu szablonów bootstrap! ? Dzięki

hugsbrugs
źródło
Myślę, że powinieneś modelować swoje dane w sposób, który pasuje do twojego projektu, prawdopodobnie powinna to być wielowymiarowa tablica lub obiekt, z reprezentacją wierszy i kolumn, następnie powinieneś iterować po wierszach i użyć dyrektywy warunkowej klasy "ng-class" i wewnątrz wiersza powinieneś wtedy iterować po kolumnach.
antanas_sepikas
Ciekawe i na pewno działające rozwiązanie, ale w dniu, w którym chcę wyświetlić 4 produkty w rzędzie zamiast 3, muszę zmodyfikować strukturę danych, wolałbym, aby to pozostało w "zakresie" czystej funkcjonalności wyświetlania ...
hugsbrugs
Rozumiem, więc prawdopodobnie powinieneś iterować fragmentami, tak jak w odpowiedzi Ariel, również ten post stackoverflow.com/questions/18564888/ może być przydatny.
antanas_sepikas
Myślę, że właśnie tego szukasz: stackoverflow.com/a/30426750/1943442
user1943442

Odpowiedzi:

164

Najczęściej głosowana odpowiedź, choć skuteczna, nie jest tym, co uważam za metodę kątową, ani nie jest to użycie własnych klas bootstrap, które mają poradzić sobie z tą sytuacją. Jak wspomniał @claies, .clearfixklasa jest przeznaczona do takich sytuacji. Moim zdaniem najczystsza realizacja wygląda następująco:

<div class="row">
    <div ng-repeat="product in products">
        <div class="clearfix" ng-if="$index % 3 == 0"></div>
        <div class="col-sm-4">
            <h2>{{product.title}}</h2>
        </div>
    </div>
</div>

Ta struktura pozwala uniknąć bałaganu w indeksowaniu tablicy products, pozwala na czystą notację kropkową i wykorzystuje klasę clearfix zgodnie z jej przeznaczeniem.

Duncan
źródło
6
Jest to dobry pomysł, jednak jeśli jesteś zainteresowany używaniem flexboksa, musisz użyć tego w wierszu, a nie na elementach div wewnątrz wierszy, aby każdy box / div miał taką samą wysokość. Clearfix jest świetny, ale nie pomaga w utrzymaniu wszystkiego w porządku.
Kontener kodowany
3
To moja ulubiona odpowiedź. Bardzo czysty i łatwy.
Hinrich
1
Świetnie sprawdza się również w mojej realizacji! :)
Will Strohl
1
Świetnie ... To powinno zostać uznane za zaakceptowaną odpowiedź!
Velu,
3
To idealna odpowiedź
Deepu
148

Wiem, że jest trochę za późno, ale i tak może komuś pomóc. Zrobiłem to tak:

<div ng-repeat="product in products" ng-if="$index % 3 == 0" class="row">
    <div class="col-xs-4">{{products[$index]}}</div>
    <div class="col-xs-4" ng-if="products.length > ($index + 1)">{{products[$index + 1]}}</div>
    <div class="col-xs-4" ng-if="products.length > ($index + 2)">{{products[$index + 2]}}</div>
</div>

jsfiddle

alpar
źródło
3
to faktycznie bardzo mi pomogło !! Dzięki.
SupimpaAllTheWay
To naprawdę łatwe do wdrożenia! Dzięki!
Manas Bajaj
Bardzo mi pomogło. Dzięki!
Ahmad Ajmi
17
Świetne rozwiązanie, jednak nie sprawdza, czy $ index + 1 i $ index +2 są poza granicami tablicy. Ostatnie dwa divy wymagają ng-if="$index+1 < products.length"ing-if="$index+2 < products.length"
Mustafa Ozturk
Czy możemy to zrobić dla pary klucz, wartość. otrzymujesz następny klucz? Zamiast dostać $ index + 1
bpbhat77
25

Okay, to rozwiązanie jest znacznie prostsze niż te, które już są dostępne i umożliwia różne szerokości kolumn dla różnych szerokości urządzeń.

<div class="row">
    <div ng-repeat="image in images">
        <div class="col-xs-6 col-sm-4 col-md-3 col-lg-2">
            ... your content here ...
        </div>
        <div class="clearfix visible-lg" ng-if="($index + 1) % 6 == 0"></div>
        <div class="clearfix visible-md" ng-if="($index + 1) % 4 == 0"></div>
        <div class="clearfix visible-sm" ng-if="($index + 1) % 3 == 0"></div>
        <div class="clearfix visible-xs" ng-if="($index + 1) % 2 == 0"></div>
    </div>
</div>

Zauważ, że % 6część ma być równa liczbie wynikowych kolumn. Więc jeśli w elemencie kolumny masz klasę col-lg-2będzie 6 kolumn, więc użyj ... % 6.

Ta technika (z wyłączeniem ng-if) jest faktycznie udokumentowana tutaj: Dokumentacja Bootstrap


źródło
1
Moim zdaniem to najlepsze rozwiązanie.
user216661
Jeśli jesteś nowy w bootstrapie, łatwo przeoczyć fakt, że definiowanie wierszy nie jest wymagane. To działało doskonale i jest nieco bardziej kompletną wersją rozwiązania Duncana.
fosflait
To jest dokładnie to, czego szukałem.
Hinrich
@phosplait Jaka jest przewaga tego nad Duncanem?
Jeppe
17

Chociaż to, co chcesz osiągnąć, może być przydatne, istnieje inna opcja, o której myślę, że możesz przeoczyć, która jest znacznie prostsza.

Masz rację, tabele Bootstrap działają dziwnie, gdy masz kolumny, które nie mają stałej wysokości. Istnieje jednak klasa ładowania początkowego utworzona w celu zwalczania tego problemu i wykonywania responsywnych resetów .

po prostu utwórz pusty <div class="clearfix"></div>przed rozpoczęciem każdego nowego wiersza, aby umożliwić zresetowanie elementów zmiennoprzecinkowych i powrót kolumn na właściwe pozycje.

tutaj jest but .

Claies
źródło
To nie rozwiązuje ujemnego 15px marginesu, który każdy wiersz ma dla ładowania początkowego.
Logus
Czy to działa w przypadku, gdy flexkolumny mają taką samą wysokość?
Alisson
16

Dzięki za sugestie, trafiłeś na właściwą drogę!

Przejdźmy do pełnego wyjaśnienia:

  • Domyślnie zapytanie http get w AngularJS zwraca obiekt

  • Więc jeśli chcesz użyć funkcji @Ariel Array.prototype.chunk, musisz najpierw przekształcić obiekt w tablicę.

  • A następnie użycie funkcji chunk W SWOIM KONTROLERZE w przeciwnym razie, jeśli zostanie użyta bezpośrednio do ng-repeat, doprowadzi cię do błędu infdig . Ostatni kontroler wygląda:

    // Initialize products to empty list
    $scope.products = [];
    
    // Load products from config file
    $resource("/json/shoppinglist.json").get(function (data_object)
    {
        // Transform object into array
        var data_array =[];
        for( var i in data_object ) {
            if (typeof data_object[i] === 'object' && data_object[i].hasOwnProperty("name")){
                data_array.push(data_object[i]);
            }
        }
        // Chunk Array and apply scope
        $scope.products = data_array.chunk(3);
    });

A HTML staje się:

<div class="row" ng-repeat="productrow in products">

    <div class="col-sm-4" ng-repeat="product in productrow">

Z drugiej strony zdecydowałem się bezpośrednio zwrócić tablicę [] zamiast obiektu {} z mojego pliku JSON. W ten sposób kontroler stanie się (proszę zwrócić uwagę na specyficzną składnię toArray: true ):

    // Initialize products to empty list 
    $scope.products = [];

    // Load products from config file
    $resource("/json/shoppinglist.json").query({method:'GET', isArray:true}, function (data_array)
    {
        $scope.products = data_array.chunk(3);
    });

HTML pozostaje taki sam jak powyżej.

OPTYMALIZACJA

Ostatnie pytanie w napięciu brzmi: jak zrobić to w 100% AngularJS bez rozszerzania tablicy javascript o funkcję chunk ... jeśli niektórzy są zainteresowani pokazaniem nam, czy ng-repeat-start i ng-repeat-end są do zrobienia. . Jestem ciekawy ;)

ROZWIĄZANIE ANDREWA

Dzięki @Andrew wiemy teraz, że dodawanie klasy bootstrap clearfix co trzy (lub jakikolwiek inny numer) elementów rozwiązuje problem z wyświetlaniem z różnych wysokości bloku.

Więc HTML staje się:

<div class="row">

    <div ng-repeat="product in products">

        <div ng-if="$index % 3 == 0" class="clearfix"></div>

        <div class="col-sm-4"> My product descrition with {{product.property}}

A twój kontroler pozostaje dość miękki dzięki usuniętej funkcji chunck :

// Initialize products to empty list 
        $scope.products = [];

        // Load products from config file
        $resource("/json/shoppinglist.json").query({method:'GET', isArray:true}, function (data_array)
        {
            //$scope.products = data_array.chunk(3);
            $scope.products = data_array;
        });
hugsbrugs
źródło
7

Możesz to zrobić bez dyrektywy, ale nie jestem pewien, czy to najlepszy sposób. Aby to zrobić, musisz utworzyć tablicę tablic z danych, które chcesz wyświetlić w tabeli, a następnie użyć 2 ng-repeat do iteracji po tablicy.

aby utworzyć tablicę do wyświetlania, użyj tej funkcji jak w przypadku produktów.chunk (3)

Array.prototype.chunk = function(chunkSize) {
    var array=this;
    return [].concat.apply([],
        array.map(function(elem,i) {
            return i%chunkSize ? [] : [array.slice(i,i+chunkSize)];
        })
    );
}

a następnie zrób coś takiego, używając powtórzenia 2 ng

<div class="row" ng-repeat="row in products.chunk(3)">
  <div class="col-sm4" ng-repeat="item in row">
    {{item}}
  </div>
</div>
Ariel Henryson
źródło
7

Oparte na rozwiązaniu Alpar, przy użyciu tylko szablonów z wspomaganymi powtórzeniami ng. Działa zarówno z pełnymi, jak i częściowo pustymi wierszami:

<div data-ng-app="" data-ng-init="products='soda','beer','water','milk','wine']" class="container">
    <div ng-repeat="product in products" ng-if="$index % 3 == 0" class="row">
        <div class="col-xs-4" 
            ng-repeat="product in products.slice($index, ($index+3 > products.length ? 
            products.length : $index+3))"> {{product}}</div>
    </div>
</div>

JSFiddle

Juangui Jordán
źródło
5

Właśnie stworzyłem rozwiązanie, które działa tylko w szablonie. Rozwiązaniem jest

    <span ng-repeat="gettingParentIndex in products">
        <div class="row" ng-if="$index<products.length/2+1">    <!-- 2 columns -->
            <span ng-repeat="product in products">
                <div class="col-sm-6" ng-if="$index>=2*$parent.$index && $index <= 2*($parent.$index+1)-1"> <!-- 2 columns -->
                    {{product.foo}}
                </div>
            </span>
        </div>
    </span>

Punkt używa danych dwukrotnie, jeden dla pętli zewnętrznej. Dodatkowe znaczniki zakresu pozostaną, ale zależy to od tego, jak sobie radzisz.

Jeśli jest to układ 3-kolumnowy, będzie wyglądał jak

    <span ng-repeat="gettingParentIndex in products">
        <div class="row" ng-if="$index<products.length/3+1">    <!-- 3 columns -->
            <span ng-repeat="product in products">
                <div class="col-sm-4" ng-if="$index>=3*$parent.$index && $index <= 3*($parent.$index+1)-1"> <!-- 3 columns -->
                    {{product.foo}}
                </div>
            </span>
        </div>
    </span>

Szczerze, chciałem

$index<Math.ceil(products.length/3)

Chociaż to nie zadziałało.

wataru
źródło
Wypróbowałem to rozwiązanie, aby zaimplementować 2 elementy w każdym rzędzie. Na przykład mam 5 elementów na liście, więc wynik powinien mieć 3 wiersze z 2 elementami / kolumnami w pierwszych 2 wierszach i 1 kolumną w ostatnim wierszu. Problem polega na tym, że otrzymuję tutaj 5 wierszy, a ostatnie 2 wiersze są puste. Zastanawiasz się, jak to naprawić? Dzięki
Maverick Riz
@MaverickAzy dzięki za próbę. Wiem, że jest problem, że jeśli wysokość tych elementów jest inna, to nie działa dobrze.
wataru
Wysokość elementów jest w rzeczywistości taka sama. Problem polega na tym, że powinienem uzyskać tylko 3 wiersze, ale otrzymuję 5 wierszy z ostatnimi 2 pustymi wierszami. Czy możesz mi powiedzieć, czy długość produktów wynosi 5, a następnie 5/2 + 1 =? Ta logika nie jest dla mnie jasna w linii nr 2 w rzędzie klasowym.
Maverick Riz
@MaverickAzy te puste wiersze muszą zostać wygenerowane w takiej postaci, w jakiej są. Czy to psuje twój układ?
wataru
nie, to nie psuje układu. Jedynym problemem są puste wiersze. Naprawdę doceniam, jeśli możesz mi w tym pomóc. Dzięki
Maverick Riz
5

Kolejne małe ulepszenie odpowiedzi @Duncan i innych odpowiedzi opartych na elemencie clearfix. Jeśli chcesz, aby treść była klikalna , potrzebujesz na niej z-index> 0 lub clearfix będzie nakładał się na treść i obsługiwał kliknięcie.

Oto przykład, który nie działa (nie widać wskaźnika kursora, a kliknięcie nic nie da):

<div class="row">
    <div ng-repeat="product in products">
        <div class="clearfix" ng-if="$index % 3 == 0"></div>
        <div class="col-sm-4" style="cursor: pointer" ng-click="doSomething()">
            <h2>{{product.title}}</h2>
        </div>
    </div>
</div>

Chociaż jest to naprawione :

<div class="row">
    <div ng-repeat-start="product in products" class="clearfix" ng-if="$index % 3 == 0"></div>
    <div ng-repeat-end class="col-sm-4" style="cursor: pointer; z-index: 1" ng-click="doSomething()">
            <h2>{{product.title}}</h2>
    </div>
</div>

Dodałem z-index: 1, że zawartość podnosi się ponad clearfix i usunąłem element div kontenera, używając zamiast tego ng-repeat-starti ng-repeat-end(dostępny w AngularJS 1.2), ponieważ spowodował, że indeks Z nie działał.

Mam nadzieję że to pomoże!

Aktualizacja

Plunker: http://plnkr.co/edit/4w5wZj

McGiogen
źródło
Czy to działa flexw przypadku rzędów, aby kolumny miały tę samą wysokość?
Alisson
Nie jestem pewien, czy rozumiem twoje pytanie. To jest szybki plunker pokazujący, co robi ten kod: plnkr.co/edit/4w5wZj?p=preview . Innymi słowy, clearfix poprawnie wyrównuje drugą linię tytułów: wszystkie zaczynają się od tego samego punktu, ale nadal nie mają tej samej wysokości (co widać dzięki kolorowi tła). Spróbuj usunąć klasę clearfix, aby zobaczyć, jakie jest domyślne zachowanie. Użyłem flexboksa tylko raz lub dwa razy, ale ma wiele właściwości CSS i jestem pewien, że możesz znaleźć to, czego szukasz.
McGiogen
bootstrap przedstawia przykład, jak ustawić wszystkie kolumny w tym samym wierszu, aby uzyskać taką samą wysokość najwyższej kolumny. Musiałem tego użyć. Problem polega na tym, że traci możliwość zawijania do nowej linii, gdy jest więcej niż 12 kolumn, więc musisz ręcznie tworzyć nowe wiersze. Po dokładniejszych badaniach mogłem znaleźć rozwiązanie i zamieścić tutaj jako odpowiedź, chociaż nie wiem, czy jest najlepsze. W każdym razie dzięki, Twoja odpowiedź mi pomogła!
Alisson
Pierwszy raz widzę ten przykład, jest naprawdę przydatny! Cieszę się że mogę pomóc.
McGiogen,
4

rozwiązałem to za pomocą klasy ng

<div ng-repeat="item in items">
    <div ng-class="{ 'row': ($index + 1) % 4 == 0 }">
        <div class="col-md-3">
            {{item.name}}
        </div>
    </div>
</div>
Josip
źródło
2

Najlepszym sposobem zastosowania klasy jest użycie ng-class, które może być użyte do zastosowania klas na podstawie jakiegoś warunku.

<div ng-repeat="product in products">
   <div ng-class="getRowClass($index)">
       <div class="col-sm-4" >
           <!-- your code -->
       </div>
   </div>

a następnie w kontrolerze

$scope.getRowClass = function(index){
    if(index%3 == 0){
     return "row";
    }
}
Rajiv
źródło
2

Po połączeniu wielu odpowiedzi i sugestii tutaj, jest to moja ostateczna odpowiedź, która działa dobrze flex, która pozwala nam tworzyć kolumny o równej wysokości, sprawdza również ostatni indeks i nie musisz powtarzać wewnętrznego HTML. Nie używa clearfix:

<div ng-repeat="prod in productsFiltered=(products | filter:myInputFilter)" ng-if="$index % 3 == 0" class="row row-eq-height">
    <div ng-repeat="i in [0, 1, 2]" ng-init="product = productsFiltered[$parent.$parent.$index + i]"  ng-if="$parent.$index + i < productsFiltered.length" class="col-xs-4">
        <div class="col-xs-12">{{ product.name }}</div>
    </div>
</div>

Wyświetli coś takiego:

<div class="row row-eq-height">
    <div class="col-xs-4">
        <div class="col-xs-12">
            Product Name
        </div>
    </div>
    <div class="col-xs-4">
        <div class="col-xs-12">
            Product Name
        </div>
    </div>
    <div class="col-xs-4">
        <div class="col-xs-12">
            Product Name
        </div>
    </div>
</div>
<div class="row row-eq-height">
    <div class="col-xs-4">
        <div class="col-xs-12">
            Product Name
        </div>
    </div>
    <div class="col-xs-4">
        <div class="col-xs-12">
            Product Name
        </div>
    </div>
    <div class="col-xs-4">
        <div class="col-xs-12">
            Product Name
        </div>
    </div>
</div>
Alissona
źródło
1

Mała modyfikacja w rozwiązaniu @alpar

<div data-ng-app="" data-ng-init="products=['A','B','C','D','E','F', 'G','H','I','J','K','L']" class="container">
    <div ng-repeat="product in products" ng-if="$index % 6 == 0" class="row">
        <div class="col-xs-2" ng-repeat="idx in [0,1,2,3,4,5]">
        {{products[idx+$parent.$index]}} <!-- When this HTML is Big it's useful approach -->
        </div>
    </div>
</div>

jsfiddle

jaym
źródło
0

To zadziałało dla mnie, bez splatania ani niczego wymaganego:

HTML

<div class="row" ng-repeat="row in rows() track by $index">
    <div class="col-md-3" ng-repeat="item in items" ng-if="indexInRange($index,$parent.$index)"></div>
</div>

JavaScript

var columnsPerRow = 4;
$scope.rows = function() {
  return new Array(columnsPerRow);
};
$scope.indexInRange = function(columnIndex,rowIndex) {
  return columnIndex >= (rowIndex * columnsPerRow) && columnIndex < (rowIndex * columnsPerRow) + columnsPerRow;
};
robbymarston
źródło
0

Born Solutions to najlepsze rozwiązanie, wystarczy trochę poprawić, aby sprostać potrzebom, miałem różne responsywne rozwiązania i trochę się zmieniłem

<div ng-repeat="post in posts">
    <div class="vechicle-single col-lg-4 col-md-6 col-sm-12 col-xs-12">
    </div>
    <div class="clearfix visible-lg" ng-if="($index + 1) % 3 == 0"></div>
    <div class="clearfix visible-md" ng-if="($index + 1) % 2 == 0"></div>
    <div class="clearfix visible-sm" ng-if="($index + 1) % 1 == 0"></div>
    <div class="clearfix visible-xs" ng-if="($index + 1) % 1 == 0"></div>
</div>
Kiko Seijo
źródło
0

Opierając się na odpowiedzi Alpara, oto bardziej uogólniony sposób podziału pojedynczej listy elementów na wiele kontenerów (wiersze, kolumny, zasobniki, cokolwiek):

<div class="row" ng-repeat="row in [0,1,2]">
  <div class="col" ng-repeat="item in $ctrl.items" ng-if="$index % 3 == row">
    <span>{{item.name}}</span>
  </div>
</div> 

dla listy 10 pozycji generuje:

<div class="row">
  <div class="col"><span>Item 1</span></div>
  <div class="col"><span>Item 4</span></div>
  <div class="col"><span>Item 7</span></div>
  <div class="col"><span>Item 10</span></div>
</div> 
<div class="row">
  <div class="col"><span>Item 2</span></div>
  <div class="col"><span>Item 5</span></div>
  <div class="col"><span>Item 8</span></div>
</div> 
<div class="row">
  <div class="col"><span>Item 3</span></div>
  <div class="col"><span>Item 6</span></div>
  <div class="col"><span>Item 9</span></div>
</div> 

Liczbę pojemników można szybko zakodować w funkcji kontrolera:

JS (ES6)

$scope.rowList = function(rows) {
  return Array(rows).fill().map((x,i)=>i);
}
$scope.rows = 2;

HTML

<div class="row" ng-repeat="row in rowList(rows)">
  <div ng-repeat="item in $ctrl.items" ng-if="$index % rows == row">
    ...

Takie podejście pozwala uniknąć powielania znaczników pozycji ( <span>{{item.name}}</span>w tym przypadku) w szablonie źródłowym - nie jest to wielka wygrana dla prostego zakresu, ale dla bardziej złożonej struktury DOM (którą miałem) pomaga to utrzymać szablon SUCHY.

Mark Chitty
źródło
0

Aktualizacja 2019 - Bootstrap 4

Ponieważ Bootstrap 3 używał pływaków, wymagało to clearfix resetowania co n (3 lub 4) kolumn ( .col-*) w.row aby zapobiec nierównomiernemu zawijaniu kolumn.

Teraz, gdy Bootstrap 4 używa flexboksa , nie ma już potrzeby zawijania kolumn oddzielnymi .rowtagami lub wstawiania dodatkowych elementów div, aby wymusić na colach zawijanie co n kolumn.

Możesz po prostu powtórzyć wszystkie kolumny w jednym .rowpojemniku.

Na przykład 3 kolumny w każdym wierszu wizualnym to:

<div class="row">
     <div class="col-4">...</div>
     <div class="col-4">...</div>
     <div class="col-4">...</div>
     <div class="col-4">...</div>
     <div class="col-4">...</div>
     <div class="col-4">...</div>
     <div class="col-4">...</div>
     (...repeat for number of items)
</div>

Więc w przypadku Bootstrapa, powtórzenie ng to po prostu:

  <div class="row">
      <div class="col-4" ng-repeat="item in items">
          ... {{ item }}
      </div>
  </div>

Demo: https://www.codeply.com/go/Z3IjLRsJXX

Zim
źródło
0

Zrobiłem to tylko za pomocą boostrapa, musisz bardzo uważać na położenie rzędu i kolumny, oto mój przykład.

<section>
<div class="container">
        <div ng-app="myApp">
        
                <div ng-controller="SubregionController">
                    <div class="row text-center">
                        <div class="col-md-4" ng-repeat="post in posts">
                            <div >
                                <div>{{post.title}}</div>
                            </div>
                        </div>
                    
                    </div>
                </div>        
        </div>
    </div>
</div> 

</section>

Alexis Suárez
źródło