web-dev-qa-db-fra.com

Angular ng-repeat ajoute une ligne bootstrap tous les 3 ou 4 colonnes

Je cherche le bon modèle pour injecter une classe de lignes d'amorçage toutes les 3 colonnes. J'ai besoin de ça parce que cols n'a pas de hauteur fixe (et je ne veux pas en réparer une), donc ça casse ma conception! 

Voici mon code:

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

Mais il n’affiche qu’un seul produit dans chaque ligne. Ce que je veux comme résultat final est:

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

Puis-je y parvenir avec un seul motif ng-repeat (sans directive ni contrôleur)? Le docs présente ng-repeat-start et ng-repeat-end mais je n'arrive pas à comprendre comment l'utiliser est ce cas d'utilisation! J'ai l'impression que c'est quelque chose que nous utilisons souvent dans les modèles bootstrap! ? Merci

108
hugsbrugs

La réponse votée par le haut, bien qu'efficace, n'est pas ce que je considérerais comme étant la méthode angulaire, pas plus qu'elle n'utilise les classes de bootstrap propres à gérer cette situation. Comme @claies l'a mentionné, la classe .clearfix est destinée à de telles situations. À mon avis, la mise en œuvre la plus propre est la suivante:

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

Cette structure évite l’indexation désordonnée du tableau de produits, permet une notation par points propre et utilise la classe clearfix dans le but pour lequel elle est conçue.

154
Duncan

Je sais que c'est un peu tard, mais cela pourrait toujours aider quelqu'un. Je l'ai fait comme ça:

<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

146
alpar

OK, cette solution est far plus simple que celles déjà présentes et autorise différentes largeurs de colonne pour différentes largeurs de périphériques.

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

Notez que la partie % 6 est supposée correspondre au nombre de colonnes obtenues. Ainsi, si vous avez la classe col-lg-2 dans l'élément de colonne, il y aura 6 colonnes, utilisez donc ... % 6.

Cette technique (à l'exception du ng-if) est actuellement documentée ici: Documents d'amorçage

24
user3638471

Bien que ce que vous souhaitiez accomplir puisse être utile, il existe une autre option beaucoup plus simple que vous ne voudrez probablement pas oublier.

Vous avez raison, les tables Bootstrap agissent étrangement lorsque vous avez des colonnes dont la hauteur n'est pas fixe. Cependant, une classe d'amorçage a été créée pour lutter contre ce problème et effectuer réinitialisations réactives .

créez simplement un <div class="clearfix"></div> vide avant le début de chaque nouvelle ligne pour permettre aux flotteurs de se réinitialiser et aux colonnes de reprendre leur position correcte.

voici un bootply

17
Claies

Merci pour vos suggestions, vous m'avez mis sur le bon chemin!

Partons pour une explication complète:

  • Par défaut, AngularJS http get query renvoie un objet

  • Donc, si vous voulez utiliser la fonction @Ariel Array.prototype.chunk, vous devez d'abord transformer un objet en tableau.

  • Et ensuite, utiliser la fonction de bloc IN YOUR CONTROLLER sinon, si elle est utilisée directement dans ng-repeat, cela vous mènera à une erreur infdig error . Le contrôleur final a l'air:

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

Et HTML devient:

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

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

De l'autre côté, j'ai décidé de renvoyer directement un tableau [] au lieu d'un objet {} de mon fichier JSON. De cette façon, le contrôleur devient (veuillez noter la syntaxe spécifique isArray: 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 reste le même que ci-dessus. 

OPTIMISATION

La dernière question en suspens est: comment le rendre 100% AngularJS sans étendre le tableau javascript avec la fonction chunk ... si certaines personnes sont intéressées à nous montrer si ng-repeat-start et ng-repeat-end sont le chemin à parcourir .. . Je suis curieux ;)

_ {LA SOLUTION D'ANDREW} _

Grâce à @Andrew, nous savons maintenant que l'ajout d'une classe de bootstrap clearfix tous les trois (ou n'importe quel nombre) corrige le problème d'affichage de la hauteur de différents blocs.

Alors HTML devient:

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

Et votre contrôleur reste assez souple avec la fonction enlevé

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

Basé sur la solution Alpar, n'utilisant que des modèles avec une répétition négative. Fonctionne avec les lignes pleines et partiellement vides:

<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

7
Juangui Jordán

Vous pouvez le faire sans directive mais je ne suis pas sûr que ce soit le meilleur moyen . Pour ce faire, vous devez créer un tableau de tableau à partir des données que vous voulez afficher dans la table, -repeat pour parcourir le tableau.

pour créer le tableau d'affichage, utilisez cette fonction comme celle de 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)];
        })
    );
}

et ensuite faire quelque chose comme ça en utilisant 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>
6
Ariel Henryson

Je viens de faire une solution qui ne fonctionne que dans le modèle . La solution est

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

Point utilise les données deux fois, l’une pour une boucle extérieure . Il restera des balises d’extension, mais cela dépend de la façon dont vous l’échangez.

Si c'est une mise en page à 3 colonnes, ça va être comme

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

Honnêtement je voulais

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

Bien que cela n'ait pas fonctionné.

5
wataru

Juste une autre petite amélioration sur @Duncan answer et les autres réponses basées sur l’élément clearfix . Si vous voulez rendre le contenu clickable, vous aurez besoin d’un z-index> 0 ou clearfix chevauchera le contenu et gérer le clic.

Ceci est l'exemple ne fonctionne pas (vous ne pouvez pas voir le pointeur du curseur et cliquer ne fera rien):

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

Bien que cela soit le fixe:

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

J'ai ajouté z-index: 1 pour que le contenu monte par dessus le clearfix et j'ai supprimé le conteneur div en utilisant plutôt ng-repeat-start et ng-repeat-end (disponible dans AngularJS 1.2) car cela faisait que z-index ne fonctionnait pas.

J'espère que cela t'aides!

Mettre à jour

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

5
McGiogen

j'ai résolu cela en utilisant 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>
4
Josip

La meilleure façon d'appliquer une classe est d'utiliser ng-class.Il peut être utilisé pour appliquer des classes en fonction de certaines conditions.

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

et ensuite dans votre contrôleur

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

Petite modification dans la solution de @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

1
jaym

Après avoir combiné de nombreuses réponses et suggestions ici, voici ma réponse finale, qui fonctionne bien avec flex, ce qui nous permet de créer des colonnes de même hauteur. Elle vérifie également le dernier index et vous n'avez pas besoin de répéter le code HTML interne. Il n'utilise pas 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>

Il va sortir quelque chose comme ça:

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

Born Solutions son meilleur, juste besoin d'un peu tweek aux besoins, j'ai eu différentes solutions réactives et changé un peu

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

Cela a fonctionné pour moi, pas d'épissure ou quoi que ce soit requis:

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
robbymarston

Mise à jour 2019 - Bootstrap 4

Depuis que Bootstrap 3 utilisait float, il a fallu clearfix reset every n (3 ou 4) colonnes (.col-*) dans le .row pour éviter un retour à la ligne inégal des colonnes.

Maintenant que Bootstrap 4 utilise flexbox , il n’est plus nécessaire d’encapsuler les colonnes dans des balises .row distinctes ni d’insérer des divs supplémentaires pour forcer les colonnes à encapsuler toutes les n colonnes.

Vous pouvez simplement répéter tout des colonnes dans un seul conteneur .row.

Par exemple, 3 colonnes dans chaque ligne visuelle sont:

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

Donc pour Bootstrap le ng-repeat est simplement:

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

Démo: https://www.codeply.com/go/Z3IjLRsJXX

0
Zim

Je l'ai fait uniquement en utilisant bootstrap, vous devez faire très attention à l'emplacement de la ligne et de la colonne, voici mon exemple.

<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>
0
Alexis Suárez

Essayez cet exemple 

<!DOCTYPE html>
<html>
<head>
	<title></title>

	<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
	<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
	<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular.min.js"></script>
	<script type="text/javascript">
		var app = angular.module('myApp', []);

		app.controller('myCtrl', function ($scope,$filter) {
			$scope.products =[{
				id:1,
				name:'aa',
			},{
				id:2,
				name:'bb',
			},{
				id:3,
				name:'cc',
			},{
				id:4,
				name:'dd',
			},{
				id:5,
				name:'ee',
			}];
		});

	</script>
</head>
<body ng-app="myApp">
	<div ng-controller="myCtrl">
		<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>
	</div>
</body>
</html>

0
Saurabh Agrawal