Я ищу правильный шаблон для ввода класса строк начальной загрузки каждые три столбца. Мне нужно это, потому что cols не имеет фиксированной высоты (и я не хочу ее исправлять), поэтому он нарушает мой дизайн!
Вот мой код:
<div ng-repeat="product in products">
<div ng-if="$index % 3 == 0" class="row">
<div class="col-sm-4" >
...
</div>
</div>
</div>
Но он отображает только один продукт в каждой строке. В конечном итоге я хочу:
<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>
Могу ли я достичь этого только с шаблоном ng-repeat (без директивы или контроллера)? docs вводят ng-repeat-start и ng-repeat-end, но я не могу понять, как использовать его в этом случае! Я чувствую, что это то, что мы часто используем в шаблонах бутстрапа!? Благодаря
Высший голосовой ответ, хотя и эффективный, не тот, который я бы рассматривал как способ angular, и не использовал собственные классы bootstrap, предназначенные для решения этой проблемы. Как отмечалось в @claies, класс .clearfix
предназначен для таких ситуаций. По моему мнению, самая чистая реализация выглядит следующим образом:
<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>
Эта структура позволяет избежать беспорядочной индексации массива продуктов, позволяет использовать чистую точечную нотацию и использует класс clearfix по назначению.
Я знаю это немного поздно, но это все равно может помочь кому-то. Я сделал это так:
<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 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>
Обратите внимание, что часть % 6
должна равняться числу полученных столбцов. Так что если в элементе столбца у вас есть класс col-lg-2
, то будет 6 столбцов, поэтому используйте ... % 6
.
Этот метод (исключая ng-if
) на самом деле документирован здесь: Документы Bootstrap
В то время как то, что вы хотите выполнить, может быть полезно, есть еще один вариант, который, я считаю, вы можете игнорировать, что намного проще.
Вы правы, таблицы Bootstrap действуют странно, когда у вас есть столбцы, которые не являются фиксированной высотой. Тем не менее, для устранения этой проблемы создан класс начальной загрузки и выполняется отзывчивый сброс.
просто создайте пустой <div class="clearfix"></div>
перед началом каждой новой строки, чтобы позволить поплавкам reset и столбцы вернулись в правильные позиции.
здесь bootply.
flex
чтобы сделать столбцы одинаковой высоты?
Спасибо за ваши предложения, вы меня поняли правильно!
Отпустим полное объяснение:
По умолчанию HTTP-запрос GetText возвращает объект
Итак, если вы хотите использовать функцию @Ariel Array.prototype.chunk, вы должны сначала преобразовать объект в массив.
И затем использовать функцию chunk IN YOUR CONTROLLER, иначе, если она будет использована непосредственно в ng-repeat, она приведет вас к ошибка infdig. Конечный контроллер выглядит:
// 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);
});
И HTML становится:
<div class="row" ng-repeat="productrow in products">
<div class="col-sm-4" ng-repeat="product in productrow">
С другой стороны, я решил напрямую вернуть массив [] вместо объекта {} из моего файла JSON. Таким образом, контроллер становится (обратите внимание на конкретный синтаксис 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 остается таким же, как указано выше.
ОПТИМИЗАЦИЯ
Последний вопрос в неизвестности: как сделать это 100% AngularJS без расширения массива javascript с функцией chunk... если некоторые люди заинтересованы в том, чтобы показать нам, если ng-repeat-start и ng-repeat-end являются способом идти... Мне любопытно;)
РЕШЕНИЕ ANDREW
Благодаря @Andrew мы теперь знаем, что добавление класса очистки bootstrap каждые три (или любого другого) элемента исправляет проблему отображения с разной высоты блока.
Итак, HTML становится:
<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}}
И ваш контроллер остается довольно мягким с функцией удаленной chunck:
// 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;
});
На основе решения Alpar, используя только шаблоны с активированным ng-repeat. Работает как с полными, так и с пустыми строками:
<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>
Вы можете сделать это без директивы, но я не уверен, что это лучший способ. Для этого вы должны создать массив массива из данных, которые вы хотите отобразить в таблице, и после этого используйте 2 ng-repeat для итерации по массиву.
для создания массива для отображения используйте эту функцию, например, 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)];
})
);
}
а затем сделайте что-то подобное, используя 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>
Еще одно небольшое улучшение в @Duncan answer, а остальные ответы основаны на элементе clearfix.
Если вы хотите сделать контент кликабельным, вам понадобится z-index
> 0 или clearfix будет перекрывать содержимое и обрабатывать клик.
Это пример не работает (вы не можете видеть указатель курсора, а щелчок ничего не делает):
<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>
Пока это фиксированный:
<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>
Я добавил z-index: 1
, чтобы повысить содержание по clearfix, и я удалил контейнер div, используя вместо этого ng-repeat-start
и ng-repeat-end
(доступный от AngularJS 1.2), потому что он не работал.
Надеюсь, это поможет!
Plunker: http://plnkr.co/edit/4w5wZj
flex
в строках, чтобы сделать столбцы одинаковой высоты?
Я решил это, используя 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>
Я только что решил, что он работает только в шаблоне. Решение
<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>
Точка использует данные дважды, одна для внешнего цикла. Теги расширенного диапазона останутся, но это зависит от того, как вы торгуете.
Если это макет из трех столбцов, он будет похож на
<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>
Честно говоря, я хотел
$index<Math.ceil(products.length/3)
Хотя это не сработало.
Лучший способ применения класса - использовать ng-class.It можно использовать для применения классов на основе некоторого условия.
<div ng-repeat="product in products">
<div ng-class="getRowClass($index)">
<div class="col-sm-4" >
<!-- your code -->
</div>
</div>
а затем в вашем контроллере
$scope.getRowClass = function(index){
if(index%3 == 0){
return "row";
}
}
После объединения многих ответов и предложений здесь это мой окончательный ответ, который хорошо работает с flex
, что позволяет нам создавать столбцы с равной высотой, а также проверяет последний индекс, и вам не нужно повторять внутренний HTML. Он не использует 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>
Он выведет что-то вроде этого:
<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>
Небольшая модификация в решении @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 useful approach -->
</div>
</div>
</div>
Попробуйте этот пример
<!DOCTYPE html>
<html>
<head>
<title></title>
<link rel="stylesheet" href="/bootstrap.min.css">
<script src="/jquery.min.js"></script>
<script src="/bootstrap.min.js"></script>
<script type="text/javascript" src="/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>
Born Solutions - лучший из них, просто нужно немного подружиться с потребностями, у меня были разные гибкие решения и немного изменился
<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>
Это сработало для меня, без сращивания или чего-то еще:
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;
};
Основываясь на ответе Alpar, здесь более обобщенный способ разделить один список элементов на несколько контейнеров (строки, столбцы, ведра и т.д.):
<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>
для списка из 10 элементов, генерирует:
<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>
Количество контейнеров может быть быстро закодировано в функции контроллера:
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">
...
Этот подход позволяет избежать дублирования разметки элемента (<span>{{item.name}}</span>
в этом случае) в исходном шаблоне - не огромная победа для простого диапазона, но для более сложной структуры DOM (которая у меня была) это помогает сохранить шаблон DRY.