Самый надежный и технически правильный подход - преобразовать данные в контроллере. Вот простая функция и использование фрагментов.
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);
Тогда ваше представление будет выглядеть так:
<div class="row" ng-repeat="rows in chunkedData">
<div class="span4" ng-repeat="item in rows">{{item}}</div>
</div>
Если у вас есть какие-либо входные данные в ng-repeat
, вы, вероятно, захотите разделить / повторно объединить массивы по мере изменения данных или при отправке. Вот как это будет выглядеть в a $watch
, чтобы данные всегда были доступны в исходном объединенном формате:
$scope.$watch('chunkedData', function(val) {
$scope.data = [].concat.apply([], val);
}, true); // deep watch
Многие люди предпочитают делать это в представлении с фильтром. Это возможно, но должно использоваться только для демонстрации! Если вы добавите входные данные в это отфильтрованное представление, это вызовет проблемы, которые можно решить, но они не будут красивыми или надежными .
Проблема с этим фильтром в том, что он каждый раз возвращает новые вложенные массивы. Angular наблюдает за возвращаемым значением из фильтра. При первом запуске фильтра Angular знает значение, а затем запускает его снова, чтобы убедиться, что оно изменилось. Если оба значения совпадают, цикл завершается. В противном случае фильтр будет срабатывать снова и снова, пока они не станут одинаковыми, или Angular не поймет, что происходит бесконечный цикл дайджеста, и отключится. Поскольку новые вложенные массивы / объекты ранее не отслеживались Angular, он всегда видит возвращаемое значение как отличное от предыдущего. Чтобы исправить эти «нестабильные» фильтры, вы должны заключить фильтр в memoize
функцию. lodash
имеет memoize
функцию, и последняя версия lodash также включает chunk
функцию, поэтому мы можем создать этот фильтр очень просто, используяnpm
modules и компилируем скрипт с помощью browserify
или webpack
.
Помните: только отображение! Отфильтруйте контроллер, если вы используете входы!
Установите lodash:
npm install lodash-node
Создайте фильтр:
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);
});
А вот образец с этим фильтром:
<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>
Заказывайте товары по вертикали
1 4
2 5
3 6
Что касается вертикальных столбцов (список сверху вниз), а не горизонтальных (слева направо), точная реализация зависит от желаемой семантики. Списки, которые делятся неравномерно, можно распределить по-разному. Вот один из способов:
<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;
}
Однако наиболее прямой и простой способ получить столбцы - использовать столбцы CSS :
.columns {
columns: 3;
}
<div class="columns">
<div ng-repeat="item in ['a','b','c','d','e','f','g']">
{{item}}
</div>
</div>