comment diviser les données ng-repeat avec trois colonnes en utilisant bootstrap

J'utilise ng-repeat avec mon code j'ai 'n' nombre de zone de texte basé sur ng-repeat. Je veux aligner la zone de texte avec trois colonnes.

C'est mon code

<div class="control-group" ng-repeat="oneExt in configAddr.ext">
    {{$index+1}}. 
    <input type="text" name="macAdr{{$index+1}}" 
           id="macAddress" ng-model="oneExt.newValue" value=""/>
</div>
116
demandé sur m59 2014-02-08 13:50:56

19 réponses

L'approche la plus fiable et techniquement correcte consiste à transformer les données dans le contrôleur. Voici une fonction de morceau simple et l'utilisation.

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

$scope.chunkedData = chunk(myData, 3);

Alors votre vue ressemblerait à ceci:

<div class="row" ng-repeat="rows in chunkedData">
  <div class="span4" ng-repeat="item in rows">{{item}}</div>
</div>

Si vous avez des entrées dans le ng-repeat, vous voudrez probablement désactiver / rejoindre les tableaux lorsque les données sont modifiées ou lors de la soumission. Voici à quoi cela ressemblerait dans un $watch, de sorte que les données soient toujours disponibles dans le format fusionné original:

$scope.$watch('chunkedData', function(val) {
  $scope.data = [].concat.apply([], val);
}, true); // deep watch

Beaucoup de gens préférez accomplir cela dans la vue avec un filtre. C'est possible, mais ne doit être utilisé qu'à des fins d'affichage! Si vous ajoutez des entrées dans cette vue filtrée, cela causera des problèmes qui peuvent être résolus, mais qui ne sont pas jolis ou fiables .

Le problème avec ce filtre est qu'il renvoie de nouveaux tableaux imbriqués à chaque fois. Angular surveille la valeur de retour du filtre. La première fois que le filtre fonctionne, Angulaire connaît la valeur, puis l'exécute à nouveau pour s'assurer qu'il est fait en train de changer. Si les deux valeurs sont identiques, le cycle est terminé. Sinon, le filtre se déclenchera encore et encore jusqu'à ce qu'ils soient identiques, ou Angular réalise qu'une boucle de digestion infinie se produit et s'arrête. Étant donné que les nouveaux tableaux/objets imbriqués n'ont pas été suivis auparavant par Angular, la valeur de retour est toujours considérée comme différente de la précédente. Pour corriger ces filtres "instables", vous devez envelopper le filtre dans une fonction memoize. lodash a une fonction memoize et la dernière version de lodash aussi inclut une fonction chunk, donc nous pouvons créer ce filtre très simplement en utilisant les modules npm et compiler le script avec browserify ou webpack.

rappelez-vous: Affichage Seulement! Filtrer dans le contrôleur si vous utilisez des entrées!

Installer lodash:

npm install lodash-node

Créer le filtre:

var chunk = require('lodash-node/modern/array/chunk');
var memoize = require('lodash-node/modern/function/memoize');

angular.module('myModule', [])
.filter('chunk', function() {
  return memoize(chunk);
});

Et voici un échantillon avec ce filtre:

<div ng-repeat="row in ['a','b','c','d','e','f'] | chunk:3">
  <div class="column" ng-repeat="item in row">
    {{($parent.$index*row.length)+$index+1}}. {{item}}
  </div>
</div>

Commander des articles verticalement

1  4
2  5
3  6

En ce qui concerne les colonnes verticales (liste de haut en bas) plutôt que horizontale (de gauche à droite), l'implémentation exacte dépend de la sémantique souhaitée. Les listes qui se divisent inégalement peuvent être réparties de différentes manières. Voici une façon:

<div ng-repeat="row in columns">
  <div class="column" ng-repeat="item in row">
    {{item}}
  </div>
</div>
var data = ['a','b','c','d','e','f','g'];
$scope.columns = columnize(data, 3);
function columnize(input, cols) {
  var arr = [];
  for(i = 0; i < input.length; i++) {
    var colIdx = i % cols;
    arr[colIdx] = arr[colIdx] || [];
    arr[colIdx].push(input[i]);
  }
  return arr;
}

Cependant, le plus direct et simplement de moyen simple d'obtenir des colonnes est d'utiliser CSS colonnes:

.columns {
  columns: 3;
}
<div class="columns">
  <div ng-repeat="item in ['a','b','c','d','e','f','g']">
    {{item}}
  </div>
</div>
247
répondu m59 2016-03-17 15:59:24

Cette solution est très simple:

JSON:

[{id:"1",name:"testA"},{id:"2",name:"test B"},{id:"3",name:"test C"},{id:"4",name:"test D"},{id:"5",name:"test E"}]

HTML:

<div ng-controller="MyCtrl">
  <table>
    <tr ng-repeat="item in items" ng-switch on="$index % 3">
      <td ng-switch-when="0">
        {{items[$index].id}} {{items[$index].name}}
      </td>
      <td ng-switch-when="0">
        <span ng-show="items[$index+1]">
          {{items[$index+1].id}} {{items[$index+1].name}}
        </span>
      </td>
      <td ng-switch-when="0">
        <span ng-show="items[$index+2]">    
          {{items[$index+2].id}} {{items[$index+2].name}}
        </span>
      </td>
    </tr>
  </table>
</div>

Démo en violon

entrez la description de l'image ici

59
répondu sgrillon 2018-05-04 12:51:23

Une solution propre et adaptable qui ne nécessite pas de manipulation de données :

Le code HTML:

<div class="control-group" class="label"
    ng-repeat="oneExt in configAddr.ext"
    ng-class="{'new-row': startNewRow($index, columnBreak) }">
    {{$index+1}}. 
    <input type="text" name="macAdr{{$index+1}}" 
       id="macAddress{{$index}}" ng-model="oneExt.newValue" />
</div>

Le CSS:

.label {
    float: left;
    text-align: left;
}
.new-row {
    clear: left;
}

Le JavaScript:

$scope.columnBreak = 3; //Max number of colunms
$scope.startNewRow = function (index, count) {
    return ((index) % count) === 0;
};

C'est une solution simple pour rendre les données en lignes et colonnes dynamiquement sans avoir besoin de manipuler le tableau de données que vous essayez d'afficher. De Plus, si vous essayez de redimensionner la fenêtre du navigateur, vous pouvez voir la grille s'adapte automatiquement à la taille de l'écran/div.

(j'ai également ajouté un suffixe {{$index}} à votre id puisque ng-repeat va essayer de créer plusieurs éléments avec le même id si vous ne le faites pas.)

Un exemple de travail similaire

19
répondu Cumulo Nimbus 2015-01-27 23:19:46

La réponse de M59 est plutôt bonne. La seule chose que je n'aime pas à ce sujet est qu'il utilise divs pour ce qui pourrait potentiellement être des données pour une table.

Donc, en conjonction avec le filtre de m59 (réponse quelque part ci-dessus), voici comment l'afficher dans une table.

<table>
    <tr class="" ng-repeat="rows in foos | chunk:2">
        <td ng-repeat="item in rows">{{item}}</td>
    </tr>
</table>
15
répondu Jarrod 2015-02-23 22:24:56

Voici un moyen plus simple:

<table>
    <tr ng-repeat="item in lists" ng-hide="$index%2!==0">
        <td>
            <label>{{ lists[$index].name}}</label>
        </td>
        <td ng-hide="!lists[$index+1]">
            <label>{{ lists[$index+1].name}}</label>
        </td>
    </tr>
</table>

La réponse de Cumulo Nimbus est utile pour moi mais je veux que cette grille soit enveloppée par une div qui peut afficher la barre de défilement lorsque la liste est trop longue.

Pour y parvenir, j'ai ajouté style="height:200px; overflow:auto" à un div autour de la table qui le fait apparaître comme une seule colonne.

Fonctionne maintenant pour une longueur de tableau de un.

7
répondu geting 2016-10-31 10:08:57

J'ai une fonction et je l'ai stockée dans un service afin que je puisse l'utiliser partout dans mon application:

Service:

app.service('SplitArrayService', function () {
return {
    SplitArray: function (array, columns) {
        if (array.length <= columns) {
            return [array];
        };

        var rowsNum = Math.ceil(array.length / columns);

        var rowsArray = new Array(rowsNum);

        for (var i = 0; i < rowsNum; i++) {
            var columnsArray = new Array(columns);
            for (j = 0; j < columns; j++) {
                var index = i * columns + j;

                if (index < array.length) {
                    columnsArray[j] = array[index];
                } else {
                    break;
                }
            }
            rowsArray[i] = columnsArray;
        }
        return rowsArray;
    }
}

});

Contrôleur:

$scope.rows   = SplitArrayService.SplitArray($scope.images, 3); //im splitting an array of images into 3 columns

Balisage:

 <div class="col-sm-12" ng-repeat="row in imageRows">
     <div class="col-sm-4" ng-repeat="image in row">
         <img class="img-responsive" ng-src="{{image.src}}">
     </div>
 </div>
4
répondu Chancho 2014-02-25 20:02:16

Mon approche était un mélange de choses.

Mon objectif était d'avoir une grille s'adaptant à la taille de l'écran. Je voulais 3 colonnes - lg, 2 colonnes - sm et md, et 1 colonne pour xs.

Tout d'abord, j'ai créé la fonction scope suivante, en utilisant le service angular $window:

$scope.findColNumberPerRow = function() {
    var nCols;
    var width = $window.innerWidth;

    if (width < 768) {
        // xs
        nCols = 1;
    }
    else if(width >= 768 && width < 1200) {
         // sm and md
         nCols = 2
    } else if (width >= 1200) {
        // lg
        nCols = 3;
    }
    return nCols;
};

Ensuite, j'ai utilisé la classe proposée par @ Cumulo Nimbus:

.new-row {
    clear: left;
}

Dans le div contenant le ng-repeat, j'ai ajouté le resizable directive, comme expliqué dans cette page, de sorte que chaque la fenêtre de temps est redimensionnée, le service angular $window est mis à jour avec les nouvelles valeurs.

En fin de compte, dans la div répétée j'ai:

ng-repeat="user in users" ng-class="{'new-row': ($index % findColNumberPerRow() === 0) }"

S'il vous plait, faites-moi savoir les défauts de cette approche.

J'espère que cela peut être utile.

2
répondu igorauad 2015-03-25 12:37:35

Cet exemple produit un répéteur imbriqué où les données externes incluent un tableau interne que je voulais lister en deux colonnes. Le concept serait vrai pour trois colonnes ou plus.

Dans la colonne intérieure, je répète la "ligne" jusqu'à ce que la limite soit atteinte. La limite est déterminée en divisant la longueur du tableau d'éléments par le nombre de colonnes souhaité, dans ce cas par deux. La méthode de division se trouve sur le contrôleur et passe la longueur du tableau en cours en tant que paramètre. Le Tranche JavaScript (0, tableau.la fonction length / columnCount) a ensuite appliqué la limite au répéteur.

Le deuxième répéteur de colonne est alors invoqué et répète slice (array.longueur / columnCount, tableau.longueur) qui produit la seconde moitié du tableau dans la colonne deux.

<div class="row" ng-repeat="GroupAccess in UsersController.SelectedUser.Access" ng-class="{even: $even, odd: $odd}">
    <div class="col-md-12 col-xs-12" style=" padding-left:15px;">
        <label style="margin-bottom:2px;"><input type="checkbox" ng-model="GroupAccess.isset" />{{GroupAccess.groupname}}</label>
    </div>
    <div class="row" style=" padding-left:15px;">
        <div class="col-md-1 col-xs-0"></div>
        <div class="col-md-3 col-xs-2">
            <div style="line-height:11px; margin-left:2px; margin-bottom:2px;" ng-repeat="Feature in GroupAccess.features.slice(0, state.DivideBy2(GroupAccess.features.length))">
                <span class="GrpFeature">{{Feature.featurename}}</span>
            </div>
        </div>
        <div class="col-md-3 col-xs-2">
            <div style="line-height:11px; margin-left:2px; margin-bottom:2px;" ng-repeat="Feature in GroupAccess.features.slice( state.DivideBy2(GroupAccess.features.length), GroupAccess.features.length )">
                <span class="GrpFeature">{{Feature.featurename}}</span>
            </div>
        </div>
        <div class="col-md-5 col-xs-8">
        </div>
    </div>
</div>


// called to format two columns
state.DivideBy2 = function(nValue) {
    return Math.ceil(nValue /2);
};

J'espère que cela aidera à regarder la solution d'une autre manière. (PS ceci est mon premier post ici! :-))

1
répondu Harvey Mushman 2015-04-05 14:16:01

Je répare sans .ligne

<div class="col col-33 left" ng-repeat="photo in photos">
   Content here...
</div>

Et css

.left {
  float: left;
}
1
répondu selahattinunlu 2015-04-19 13:18:38

Voici une façon facile-hacky de le faire. C'est plus Manuel et finit avec un balisage désordonné. Je ne recommande pas cela, mais il y a des situations où cela pourrait être utile.

Voici un lien de violon http://jsfiddle.net/m0nk3y/9wcbpydq/

HTML:

<div ng-controller="myController">
    <div class="row">
        <div class="col-sm-4">
            <div class="well">
                <div ng-repeat="person in people = data | limitTo:Math.ceil(data.length/3)">
                {{ person.name }}
                </div>
            </div>
        </div>
        <div class="col-sm-4">
            <div class="well">
                <div ng-repeat="person in people = data | limitTo:Math.ceil(data.length/3):Math.ceil(data.length/3)">
                {{ person.name }}
                </div>
            </div>
        </div>
        <div class="col-sm-4">
            <div class="well">
                <div ng-repeat="person in people = data | limitTo:Math.ceil(data.length/3):Math.ceil(data.length/3)*2">
                {{ person.name }}
                </div>
            </div>
        </div>
    </div>
</div>

JS:

var app = angular.module('myApp', []);

app.controller('myController', function($scope) {

    $scope.Math = Math;
    $scope.data = [
        {"name":"Person A"},
        ...
    ];
});

Cette configuration nous oblige à utiliser des mathématiques sur le balisage:/, vous devrez donc injecter des mathématiques en ajoutant cette ligne: $scope.Math = Math;

1
répondu Gene Parcellano 2015-11-19 23:11:34

Toutes ces réponses semblent massivement sur machinées.

De loin la méthode la plus simple serait de configurer les divs d'entrée dans une colonne Bootstrap col-md-4, puis bootstrap le formatera automatiquement en 3 colonnes en raison de la nature de 12 colonnes de bootstrap:

<div class="col-md-12">
    <div class="control-group" ng-repeat="oneExt in configAddr.ext">
        <div class="col-md-4">
            <input type="text" name="macAdr{{$index}}"
                   id="macAddress" ng-model="oneExt.newValue" value="" />
        </div>
    </div>
</div>
1
répondu cullimorer 2015-12-17 14:06:34
<div class="row">
  <div class="col-md-4" ng-repeat="remainder in [0,1,2]">
    <ul>
      <li ng-repeat="item in items" ng-if="$index % 3 == remainder">{{item}}</li>
    </ul>
  </div>
</div>
1
répondu mrded 2016-02-03 17:50:09

Une astuce simple avec" clearfix " CSS recommandé par Bootstrap:

<div class="row">
      <div ng-repeat-start="value in values" class="col-md-4">
        {{value}}
      </div>
      <div ng-repeat-end ng-if="$index % 3 == 0" class="clearfix"></div>
</div>

De nombreux avantages: efficace, rapide, en utilisant les recommandations Boostrap, en évitant d'éventuels problèmes de $ digest ,et en ne modifiant pas le modèle angulaire.

1
répondu Lastnico 2017-12-22 14:17:08

Se basant sur la très bonne réponse de m59. J'ai trouvé que les entrées du modèle seraient floues si elles changeaient, donc vous ne pouviez changer qu'un caractère à la fois. Ceci est un nouveau pour les listes d'objets pour tous ceux qui en ont besoin:

Modifier mis à jour pour gérer plusieurs filtres sur une page

app.filter('partition', function() {
  var cache = {}; // holds old arrays for difference repeat scopes
  var filter = function(newArr, size, scope) {
    var i,
      oldLength = 0,
      newLength = 0,
      arr = [],
      id = scope.$id,
      currentArr = cache[id];
    if (!newArr) return;

    if (currentArr) {
      for (i = 0; i < currentArr.length; i++) {
        oldLength += currentArr[i].length;
      }
    }
    if (newArr.length == oldLength) {
      return currentArr; // so we keep the old object and prevent rebuild (it blurs inputs)
    } else {
      for (i = 0; i < newArr.length; i += size) {
        arr.push(newArr.slice(i, i + size));
      }
      cache[id] = arr;
      return arr;
    }
  };
  return filter;
}); 

Et ce serait l'utilisation:

<div ng-repeat="row in items | partition:3:this">
  <span class="span4">
    {{ $index }}
  </span>
</div>
0
répondu JSous 2014-06-25 19:01:22

Je suis nouveau dans bootstrap et angularjs, mais cela pourrait aussi faire un tableau par 4 éléments en tant que groupe, le résultat ressemblera presque à 3 colonnes. Cette astuce utilise le principe de ligne de rupture bootstrap.

<div class="row">
 <div class="col-sm-4" data-ng-repeat="item in items">
  <div class="some-special-class">
   {{item.XX}}
  </div>
 </div>
</div>
0
répondu Nathan 2015-02-11 14:10:33

Lodash a une méthode de chunk intégrée maintenant que j'utilise personnellement: https://lodash.com/docs#chunk

Sur cette base, le code du contrôleur peut ressembler à ce qui suit:

$scope.groupedUsers = _.chunk( $scope.users, 3 )

Code D'affichage:

<div class="row" ng-repeat="rows in groupedUsers">
  <div class="span4" ng-repeat="item in rows">{{item}}</div>
</div>
0
répondu sean2078 2015-03-27 16:28:17

Une autre façon est définie width:33.33%; float:left sur un wrapper div comme ceci:

<div ng-repeat="right in rights" style="width: 33.33%;float: left;">
    <span style="width:60px;display:inline-block;text-align:right">{{$index}}</span>
    <input type="number" style="width:50px;display:inline-block" ">
</div>
0
répondu geting 2016-03-21 02:56:40

Je me suis retrouvé dans un cas similaire, voulant générer des groupes d'affichage de 3 colonnes chacun. Cependant, bien que j'utilisais bootstrap, j'essayais de séparer ces groupes en différents divs parents. Je voulais aussi faire quelque chose d'utile génériquement.

Je l'ai approché avec 2 ng-repeat comme ci-dessous:

<div ng-repeat="items in quotes" ng-if="!($index % 3)">
  <div ng-repeat="quote in quotes" ng-if="$index <= $parent.$index + 2 && $index >= $parent.$index">
                ... some content ...
  </div>
</div>

Cela rend très facile de passer à un nombre différent de colonnes, et séparé en plusieurs divs parents.

0
répondu Matt Kindy 2017-03-22 23:15:21

Cela répond à la question initiale qui est de savoir comment obtenir 1,2,3 dans une colonne. – demandé par kuppu Février 8 ' 14 à 13: 47

Code Angularjs:

function GetStaffForFloor(floor) {
    var promiseGet = Directory_Service.getAllStaff(floor);
    promiseGet.then(function (pl) {
        $scope.staffList = chunk(pl.data, 3); //pl.data; //
    },
    function (errorOD) {
        $log.error('Errored while getting staff list.', errorOD);
    });
}
function chunk(array, columns) {
    var numberOfRows = Math.ceil(array.length / columns);

    //puts 1, 2, 3 into column
    var newRow = []; //array is row-based.
    for (var i = 0; i < array.length; i++) {
        var columnData = new Array(columns);
        if (i == numberOfRows) break;
        for (j = 0; j < columns; j++)
        {
            columnData[j] = array[i + numberOfRows * j];
        }
        newRow.push(columnData); 
    }
    return newRow;

    ////this works but 1, 2, 3 is in row
    //var newRow = [];
    //for (var i = 0; i < array.length; i += columns) {
    //    newRow.push(array.slice(i, i + columns)); //push effectively does the pivot. array is row-based.
    //}
    //return newRow;
};

Voir le Code (Remarque: En utilisant bootstrap 3):

<div class="staffContainer">
    <div class="row" ng-repeat="staff in staffList">
        <div class="col-md-4" ng-repeat="item in staff">{{item.FullName.length > 0 ? item.FullName + ": Rm " + item.RoomNumber : ""}}</div>
    </div>
</div>
-1
répondu user2378769 2015-05-26 22:29:44