web-dev-qa-db-fra.com

angular.js ng-repeat pour créer une grille

J'essaie de créer une grille en utilisant bootstrap 3 et angularjs.

La grille que j'essaie de créer est la suivante, répétée à l'aide de ng-repeat.

<div class="row">
 <div class="col-md-4">item</div>
 <div class="col-md-4">item</div>
 <div class="col-md-4">item</div>
</div>

J'ai essayé d'utiliser ng-if avec ($index % 3 == 0) pour ajouter les lignes, mais cela ne semble pas fonctionner correctement. Toute suggestion sera appréciée!

Je vous remercie!

EDIT: Voici le code que j'ai utilisé qui a fonctionné:

<div ng-repeat="item in items">
  <div ng-class="row|($index % 3 == 0)">
    <ng-include class="col-sm-4" src="'views/items/item'"></ng-include> 
  </div>
</div>
43
dzm

Ceci est une vieille réponse!

J'étais encore un peu nouveau sur Angular quand j'ai écrit ça. Ci-dessous, une réponse bien meilleure de Shivam que je vous suggère d'utiliser à la place. Cela empêche la logique de présentation de votre contrôleur, ce qui est une très bonne chose.

Réponse originale

Vous pouvez toujours scinder la liste que vous répétez en une liste de listes (de trois éléments chacune) dans votre contrôleur. Donc, votre liste est:

$scope.split_items = [['item1', 'item2', 'item3'], ['item4', 'item5', 'item6']];

Et répétez-le ensuite:

<div ng-repeat="items in split_items" class="row">
    <div ng-repeat="item in items" class="col-md-4">
        item
    </div>
</div>

Pas sûr s'il y a un meilleur moyen. J'ai aussi essayé de jouer avec ng-if et ng-switch mais je ne pouvais jamais le faire fonctionner.

20
Erik Honn

La réponse acceptée est la solution évidente, mais la logique de présentation doit rester visible et non dans les contrôleurs ou les modèles. De plus, je n'ai pas réussi à faire fonctionner la solution du PO.

Il existe deux façons de créer un système de grille lorsque vous avez une liste d'éléments (tableau) à plat… .. Dites que notre liste d'éléments est un alphabet:

Plunker ici

$scope.alphabet = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 
                   'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];

Méthode 1:

Ceci est une solution angulaire pure.

<div class="row" ng-repeat="letter in alphabet track by $index" ng-if="$index % 4 == 0">
  <div class="col-xs-3 letter-box" 
       ng-repeat="i in [$index, $index + 1, $index + 2, $index + 3]" 
       ng-if="alphabet[i] != null">
    <div>Letter {{i + 1}} is: <b> {{alphabet[i]}}</b></div>
  </div>
</div>

La boucle externe est exécutée toutes les 4 itérations et crée une ligne. Pour chaque exécution de la boucle externe, la boucle interne est itérée 4 fois et crée des colonnes. Étant donné que la boucle interne est exécutée 4 fois, que nous ayons ou non des éléments dans un tableau, le ng-if s'assure qu'aucune colonne superflue n'est créée si le tableau se termine avant la fin de la boucle interne.

Méthode 2:

Cette solution est beaucoup plus simple mais nécessite angular-filter library.

<div class="row" ng-repeat="letters in alphabet | chunkBy:4">
  <div class="col-xs-3 letter-box" ng-repeat="letter in letters" >
    <div>Letter {{$index + 1}} is: <b> {{letter}}</b></div>
  </div>
</div>

La boucle externe crée des groupes de 4 lettres, correspondant à notre "rangée"

[['A', 'B', 'C', 'D'], ['E', 'F', 'G', 'H'], ... ]

La boucle interne effectue une itération sur le groupe et crée des colonnes.

Remarque: La méthode 2 peut nécessiter une évaluation du filtre pour chaque itération de la boucle externe. Par conséquent, la méthode 2 peut ne pas évoluer correctement pour des ensembles de données volumineux.

71
CodeExpress

Vous pouvez simplement diviser votre tableau en sous-réseaux de N à l'intérieur de votre contrôleur. Exemple de code:

var array = ['A','B','C','D','E','F','G','H'];

var chunk = function(arr, size) {
   var newArr = [];
      for (var i=0; i<arr.length; i+=size) {
          newArr.Push(arr.slice(i, i+size));
      }
   return newArr;
};

$scope.array = chunk(array, 2);

Maintenant dans le fichier * .html Il vous suffit de ng-repeat à travers la array

<div class="row" ng-repeat="chunk in array">
    <div class="col-md-6" ng-repeat="item in chunk">
         {{item}}
    </div>
</div>

Cette séance d'entraînement pour moi:) Bonne chance!

9
markkillah

Utiliser ng-repeat-start et ng-repeat-end

<div class="row">
    <div ng-repeat-start="item in items track by $index" class="col-sm-4">
      {{item}}
    </div>
    <div ng-repeat-end ng-if="($index+1) % 3 == 0" class="clearfix"></div>
</div>

Facile à adapter pour différentes requêtes de médias en utilisant .visible- * classes

<div class="row">
    <div ng-repeat-start="item in items track by $index" class="col-lg-2 col-md-4 col-sm-6">
      {{item}}
    </div>
    <div ng-repeat-end>
        <div class="clearfix visible-lg-block" ng-if="($index+1) % 6 == 0"></div>
        <div class="clearfix visible-md-block" ng-if="($index+1) % 3 == 0"></div>
        <div class="clearfix visible-sm-block" ng-if="($index+1) % 2 == 0"></div>
    </div>
</div> 

Je trouve clair et concis d'avoir une logique de gestion des lignes en dehors du bloc de répétition principal. Séparation des préoccupations :-)

3
Guillaume Morin

On pourrait dire que la solution ci-dessous ne suit pas les règles de grille consistant à avoir row divs, mais une autre solution consisterait à supprimer la classe row (ou à l’utiliser en dehors du ng-repeat) et à utiliser plutôt la classe clearfix:

<div class="col-md-12">
  <div ng-repeat="item in items">
    <div ng-class="{'clearfix': $index%3 === 0}"></div>
    <div class="col-md-4">{{item}}</div>
  </div>
</div>

Autant que je sache, cela ressemble presque à celui de la classe row, mais veuillez commenter les éventuels défauts (sauf celui que j'ai mentionné ci-dessus).

3
user4776684

Réponse un peu tardive mais j’ai utilisé cela et j’estime que c’est mieux dans certains cas. Vous pouvez utiliser Angular Filter package et son filtre ChunkBy pour cela. Bien que ce paquet représente un gros travail pour cette tâche unique, il contient d’autres filtres utiles pour différentes tâches. Le code que j'ai utilisé est comme ceci: 

<div class="row mar-t2" ng-repeat="items in posts | chunkBy:3">
    <div class="col-md-4" ng-repeat="post in items">
        <img ng-src="{{post.featured_url}}" class="img-responsive" />
        <a ng-click="modalPop(post.id)"><h1 class="s04-bas2">{{post.title.rendered}}</h1></a>
        <div class="s04-spotbox2" ng-bind-html="post.excerpt.rendered"></div>
    </div>
</div>
2
heirenton

Cela devrait marcher

<div ng-repeat="item in items">
    <div ng-class="{row : ($index % 3 == 0)}">
        ... 
    </div>
</div>
0
marcinowski

J'ai pris une méthode légèrement différente en utilisant ngInit . Je ne suis pas sûr que ce soit la solution appropriée, car la documentation de ngInit indique 

La seule utilisation appropriée de ngInit concerne l'aliasing des propriétés spéciales de ngRepeat, comme indiqué dans la démonstration ci-dessous. Outre ce cas, vous devez utiliser des contrôleurs plutôt que ngInit pour initialiser les valeurs sur une étendue.

Je ne suis pas vraiment sûr que cela tombe dans ce cas, mais je voulais déplacer cette fonctionnalité loin du contrôleur pour donner au concepteur de modèles plus de liberté pour grouper par rangées avec bootstrap. Je n'ai toujours pas testé cette possibilité de liaison, mais vu que je suis un suivi par $ index, je ne pense pas que cela devrait poser problème.

J'aimerais entendre vos commentaires.

J'ai créé un filtre appelé "splitrow" qui prend un argument (le nombre d'éléments dans chaque ligne)

.filter('splitrow', function(){
    return function (input, count){
        var out = [];
            if(typeof input === "object"){
                for (var i=0, j=input.length; i < j; i+=count) {
                    out.Push(input.slice(i, i+count));
                }
            }
        return out;
    }
});

Dans le modèle de vue, j'ai organisé les lignes d'amorçage comme suit:

<div ng-init="rows = (items|splitrow:3)">
    <div ng-repeat='row in rows' class="row">
        <div ng-repeat="item in row track by $index" class="col-md-4">
            {{item.property}}
        </div>
    </div>
</div>

J'ai édité @ Shivam's Plunker pour utiliser cette méthode. Il ne nécessite aucune bibliothèque externe.

Plunker

0
user1943442

Ma solution est très similaire à celle de @CodeExpress. J'ai créé un filtre batch qui regroupe les éléments d'un tableau (son nom est emprunté au filtre homologue de Twig ) . Je ne gère pas les tableaux associatifs par souci de simplicité.

angular.module('myapp.filters', [])
    .filter('batch', function() {
        var cacheInputs = [];
        var cacheResults = [];

        return function(input, size) {
            var index = cacheInputs.indexOf(input);

            if (index !== -1) {
                return cacheResults[index];
            }

            var result = [];

            for (i = 0; i < input.length; i += size) {
                result.Push(input.slice(i, i + size));
            }

            cacheInputs.Push(input);
            cacheResults.Push(result);

            return result;
        }
    })
;

Il peut être utilisé de cette façon:

<div ng-repeat="itemsRow in items|batch:3">
    <div class="row">
        <div ng-repeat="item in itemsRow">
            <div class="col-md-4">
                ...
            </div>
        </div>
    </div>
</div>

Les résultats du filtre sont mis en cache pour éviter l'erreur 10 $digest() iterations reached. Aborting!.

0
Michaël Perrin

Une version angulaire2 de la solution angulaire pure de @ CodeExpress. 

alphabet: string[] = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 
                   'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];

<div *ngIf='alphabet && alphabet.length'>
    <div *ngFor='#letter of alphabet; #i = index' >
        <div class="row" *ngIf='i % 4 == 0'>
            <div class="col-md-3" *ngFor='#n of [i,i+1,i+2,i+3]'>
                {{alphabet[n]}}
            </div>
        </div>
    </div>
</div>
0
Alex Logan