Angular ng-repeat aggiungi una riga di bootstrap ogni 3 o 4 colonne


111

Sto cercando il modello giusto per iniettare una classe di riga bootstrap ogni 3 colonne. Ne ho bisogno perché cols non ha un'altezza fissa (e non voglio aggiustarne uno), quindi rompe il mio design!

Ecco il mio codice:

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

Ma mostra solo un prodotto in ogni riga. Quello che voglio come risultato finale è:

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

Posso ottenere questo risultato solo con il pattern ng-repeat (senza direttiva o controller)? I documenti introducono ng-repeat-start e ng-repeat-end ma non riesco a capire come usarli in questo caso d'uso! Sento che questo è qualcosa che usiamo spesso nei modelli bootstrap! ? Grazie


Penso che dovresti modellare i tuoi dati in un modo che si adatti al tuo design, dovrebbe probabilmente essere un array o un oggetto multidimensionale, con la rappresentazione di righe e colonne, quindi dovresti iterare su righe e usare la direttiva condizionale della classe "ng-class" e all'interno della riga dovresti quindi iterare sulle colonne.
antanas_sepikas

Interessante e sicuramente una soluzione funzionante, ma il giorno in cui voglio visualizzare 4 prodotti di fila invece di 3, devo modificare la mia struttura dati, preferirei che questo rimanesse nell'ambito della pura funzionalità di visualizzazione ...
hugsbrugs

Vedo, allora si dovrebbe probobly iterate in blocchi come in Ariel risposta, anche si può trovare questo post stackoverflow.com/questions/18564888/... utile.
antanas_sepikas

Penso che questo sia esattamente quello che stai cercando: stackoverflow.com/a/30426750/1943442
user1943442

Risposte:


164

La risposta più votata, sebbene efficace, non è ciò che considererei il modo angolare, né utilizza le classi di bootstrap che hanno lo scopo di affrontare questa situazione. Come menzionato da @claies, la .clearfixclasse è pensata per situazioni come queste. A mio parere, l'implementazione più pulita è la seguente:

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

Questa struttura evita l'indicizzazione disordinata dell'array di prodotti, consente una chiara notazione del punto e utilizza la classe clearfix per lo scopo previsto.


6
Questa è una buona idea, tuttavia se sei interessato a usare flexbox devi usarlo sulla riga e non sui div all'interno delle righe per consentire a ogni box / div di avere la stessa altezza. Clearfix è ottimo ma non aiuta a mantenere tutto in ordine.
Contenitore codificato

3
Questa è la mia risposta preferita. Molto pulito e facile.
Hinrich

1
Funziona alla grande anche per la mia implementazione! :)
Will Strohl

1
Fantastico ... Questa dovrebbe essere votata come una risposta accettata!
Velu

3
Questa è la risposta perfetta
Deepu

148

So che è un po 'tardi ma potrebbe comunque aiutare qualcuno. L'ho fatto così:

<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


3
in realtà mi ha aiutato molto !! Grazie.
SupimpaAllTheWay

È davvero facile da implementare! Grazie!
Manas Bajaj,

Mi ha aiutato molto. Grazie!
Ahmad Ajmi,

17
Ottima soluzione, tuttavia non controlla se $ index + 1 e $ index +2 hanno superato i limiti dell'array. Gli ultimi due div richiedono ng-if="$index+1 < products.length"eng-if="$index+2 < products.length"
Mustafa Ozturk

Possiamo farlo per chiave, coppia di valori. ottenere la chiave successiva? Invece di ottenere $ index + 1
bpbhat77

25

Ok, questa soluzione è molto più semplice di quelle già presenti e consente larghezze di colonna diverse per larghezze di dispositivo diverse.

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

Si noti che la % 6parte dovrebbe essere uguale al numero di colonne risultanti. Quindi se sull'elemento colonna hai la classe col-lg-2ci saranno 6 colonne, quindi usa ... % 6.

Questa tecnica (esclusa la ng-if) è effettivamente documentata qui: Bootstrap docs


1
Secondo me questa è la soluzione migliore.
user216661

Se sei nuovo al bootstrap, è facile trascurare il fatto che la definizione delle righe non è richiesta. Ha funzionato perfettamente ed è una versione leggermente più completa della soluzione di Duncan.
phosplait

Questo e 'esattamente quello che stavo cercando.
Hinrich

@phosplait Qual è il vantaggio di questo rispetto a Duncan?
Jeppe

17

Anche se ciò che vuoi ottenere può essere utile, c'è un'altra opzione che credo potresti trascurare che è molto più semplice.

Hai ragione, le tabelle Bootstrap si comportano in modo strano quando hai colonne che non sono ad altezza fissa. Tuttavia, esiste una classe bootstrap creata per combattere questo problema ed eseguire ripristini reattivi .

crea semplicemente un vuoto <div class="clearfix"></div>prima dell'inizio di ogni nuova riga per consentire ai float di resettarsi e alle colonne di tornare alle loro posizioni corrette.

ecco un bootply .


Questo non risolve i 15px negativi di margine che ogni .row ha per il bootstrap.
Logus

Funziona con flexper rendere le colonne della stessa altezza?
Alisson

16

Grazie per i tuoi suggerimenti, mi hai messo sulla strada giusta!

Andiamo per una spiegazione completa:

  • Per impostazione predefinita, la query http di AngularJS restituisce un oggetto

  • Quindi, se vuoi usare la funzione @Ariel Array.prototype.chunk devi prima trasformare l'oggetto in un array.

  • E poi usare la funzione chunk NEL TUO CONTROLLER altrimenti se usato direttamente in ng-repeat, ti porterà a un errore di infdig . Il controller finale sembra:

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

E l'HTML diventa:

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

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

Dall'altro lato, ho deciso di restituire direttamente un array [] invece di un oggetto {} dal mio file JSON. In questo modo, il controller diventa (si prega di notare la sintassi specifica èArray: 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);
    });

L'HTML rimane lo stesso di sopra.

OTTIMIZZAZIONE

L'ultima domanda in sospeso è: come renderlo 100% AngularJS senza estendere l'array javascript con la funzione chunk ... se alcune persone sono interessate a mostrarci se ng-repeat-start e ng-repeat-end sono la strada da percorrere .. . Sono curioso ;)

LA SOLUZIONE DI ANDREW

Grazie a @Andrew, ora sappiamo che l'aggiunta di una classe clearfix bootstrap ogni tre (o qualsiasi numero) elemento corregge il problema di visualizzazione dall'altezza del blocco diverso.

Quindi HTML diventa:

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

E il tuo controller rimane abbastanza morbido con la funzione chunck rimossa :

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

7

Puoi farlo senza una direttiva ma non sono sicuro che sia il modo migliore. Per fare ciò è necessario creare un array di array dai dati che si desidera visualizzare nella tabella, quindi utilizzare 2 ng-repeat per scorrere l'array.

per creare l'array per la visualizzazione usa questa funzione come quella products.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)];
        })
    );
}

e poi fai qualcosa del genere usando 2 ng-repeat

<div class="row" ng-repeat="row in products.chunk(3)">
  <div class="col-sm4" ng-repeat="item in row">
    {{item}}
  </div>
</div>

7

Basato sulla soluzione Alpar, utilizzando solo modelli con ripetizione ng analizzata. Funziona con righe sia piene che parzialmente vuote:

<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


5

Ho appena fatto una soluzione che funziona solo in template. La soluzione è

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

Il punto utilizza i dati due volte, uno è per un ciclo esterno. I tag span extra rimarranno, ma dipende da come si effettua il trade off.

Se è un layout a 3 colonne, sarà come

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

Onestamente lo volevo

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

Anche se non ha funzionato.


Ho provato questa soluzione per implementare 2 elementi in ogni riga. Ad esempio, ho 5 elementi in un elenco, quindi l'output che dovrei avere è 3 righe con 2 elementi / colonne nelle prime 2 righe e 1 colonna nell'ultima riga. Il problema è che sto ottenendo 5 righe qui con le ultime 2 righe vuote. Ti stai chiedendo come risolvere questo problema? Grazie
Maverick Riz

@ MaverickAzy grazie per aver provato. So che c'è un problema per cui se l'altezza di quegli elementi è diversa, non funziona bene.
wataru

L'altezza degli elementi è effettivamente la stessa. Il problema è che dovrei ottenere solo 3 righe ma ottenere 5 righe con le ultime 2 righe vuote. Puoi dirmi se products.length è 5, quindi 5/2 + 1 =? Questa logica non è chiara per me alla riga n. 2 per la riga di classe.
Maverick Riz

@MaverickAzy quelle righe vuote devono essere generate così come sono. Rovina il tuo layout?
wataru

no, non rovina il layout. L'unica preoccupazione riguarda le righe vuote. Apprezzo davvero se puoi aiutarmi con questo. Grazie
Maverick Riz

5

Solo un altro piccolo miglioramento sulla risposta di @Duncan e le altre risposte basate sull'elemento clearfix. Se vuoi rendere il contenuto cliccabile avrai bisogno di un filez-index > 0 o clearfix si sovrapporrà al contenuto e gestirà il clic.

Questo è l' esempio che non funziona (non puoi vedere il puntatore del cursore e fare clic non farà nulla):

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

Mentre questo è quello fisso :

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

Ho aggiunto z-index: 1di aumentare il contenuto rispetto a clearfix e ho rimosso il div contenitore usando invece ng-repeat-starte ng-repeat-end(disponibile da AngularJS 1.2) perché non funzionava z-index.

Spero che questo ti aiuti!

Aggiornare

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


Funziona con flexnelle righe per rendere le colonne della stessa altezza?
Alisson

Non sono sicuro di aver capito la tua domanda. Questo è un plunker veloce per mostrarti cosa fa questo codice: plnkr.co/edit/4w5wZj?p=preview . In parole, clearfix allinea correttamente la seconda riga di titoli: iniziano tutti dallo stesso punto ma non hanno ancora la stessa altezza (come puoi vedere grazie al colore di sfondo). Prova a eliminare la classe clearfix per vedere qual è il comportamento predefinito. Ho usato flexbox solo una o due volte ma ha molte proprietà CSS e sono sicuro che puoi trovare quello che stai cercando.
McGiogen

bootstrap fornisce un esempio su come creare tutte le colonne nella stessa riga per ottenere la stessa altezza della colonna più alta. Ho dovuto usare questo. Il problema è che perde la capacità di andare a capo su una nuova riga quando ci sono più di 12 colonne, quindi è necessario creare manualmente nuove righe. Dopo aver cercato un po 'di più, potrei ottenere una soluzione e postarla qui come risposta, anche se non so se sia la migliore. Grazie comunque, la tua risposta mi ha aiutato!
Alisson

È la prima volta che vedo quell'esempio, è davvero utile! Felice di aiutarti.
McGiogen

4

Ho risolto questo problema usando ng-class

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

2

Il modo migliore per applicare una classe è usare ng-class, che può essere usato per applicare classi basate su alcune condizioni.

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

e poi nel tuo controller

$scope.getRowClass = function(index){
    if(index%3 == 0){
     return "row";
    }
}

2

Dopo aver combinato molte risposte e suggerimenti qui, questa è la mia risposta finale, che funziona bene flex, che ci consente di creare colonne con la stessa altezza, controlla anche l'ultimo indice e non è necessario ripetere l'HTML interno. Non usa 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>

Produrrà qualcosa del genere:

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

1

Piccola modifica nella soluzione di @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


0

Questo ha funzionato per me, nessuna giunzione o altro richiesto:

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

0

Born Solutions è la migliore, ho solo bisogno di un po 'di tweek per soddisfare le esigenze, avevo diverse soluzioni reattive e ho cambiato un po'

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

0

Basandosi sulla risposta di Alpar, ecco un modo più generale per dividere un singolo elenco di elementi in più contenitori (righe, colonne, bucket, qualunque cosa):

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

per un elenco di 10 elementi, genera:

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

Il numero di contenitori può essere rapidamente codificato in una funzione di controller:

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

Questo approccio evita di duplicare il markup dell'elemento ( <span>{{item.name}}</span>in questo caso) nel modello di origine - non una grande vittoria per un intervallo semplice, ma per una struttura DOM più complessa (che avevo) questo aiuta a mantenere il modello ASCIUTTO.


0

Aggiornamento 2019 - Bootstrap 4

Poiché Bootstrap 3 utilizzava i float, richiedeva il reset di clearfix ogni n (3 o 4) colonne ( .col-*) nel.row per evitare il wrapping non uniforme delle colonne.

Ora che Bootstrap 4 utilizza flexbox , non è più necessario racchiudere le colonne in .rowtag separati o inserire div extra per costringere le colonne a racchiudere ogni n colonne colonne.

Puoi semplicemente ripetere tutte le colonne in un unico .rowcontenitore.

Ad esempio 3 colonne in ogni riga visiva sono:

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

Quindi per Bootstrap il ng-repeat è semplicemente:

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

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


0

L'ho fatto solo usando boostrap, devi stare molto attento nella posizione della riga e della colonna, ecco il mio esempio.

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.