I\'m trying to create a grid using bootstrap 3 and angularjs.
The grid I\'m trying to create is this, repeated using ng-repeat.
Using ng-repeat-start and 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>
Easy to adapt for different media query using .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>
I find clear and concise to have row management logic outside of the main repeat block. Separation of concerns :-)
This is an old answer!
I was still a bit new on Angular when I wrote this. There is a much better answer below from Shivam that I suggest you use instead. It keeps presentation logic out of your controller, which is a very good thing.
Original answer
You can always split the list you are repeating over into a list of lists (with three items each) in your controller. So you list is:
$scope.split_items = [['item1', 'item2', 'item3'], ['item4', 'item5', 'item6']];
And then repeat it as:
<div ng-repeat="items in split_items" class="row">
<div ng-repeat="item in items" class="col-md-4">
item
</div>
</div>
Not sure if there is a better way. I have also tried playing around with ng-if and ng-switch but I could never get it to work.
I took a slightly different method using ngInit. I'm not sure if this is the appropriate solution since the ngInit documentation states
The only appropriate use of ngInit is for aliasing special properties of ngRepeat, as seen in the demo below. Besides this case, you should use controllers rather than ngInit to initialize values on a scope.
I'm not really sure if this falls under that case, but I wanted to move this functionality away from the controller to give the template designer easier freedom to group by rows with bootstrap. I still haven't tested this for binding, but seeing as i'm tracking by $index I don't think that should be a problem.
Would love to hear feedback.
I created a filter called "splitrow" that takes one argument (the count of how many items in each row)
.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;
}
});
Within the view template I organized the bootstrap rows as follows:
<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>
I edited @Shivam's Plunker to use this method. It requires no external libraries.
Plunker
You can simply chunk your array into subarrays of N inside of your controller. Sample 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);
Now in *.html file You simply ng-repeat
through the array
<div class="row" ng-repeat="chunk in array">
<div class="col-md-6" ng-repeat="item in chunk">
{{item}}
</div>
</div>
That workout for me :) Good luck!
My solution is very similar to @CodeExpress one. I made a batch
filter that groups items of an array (the name is borrowed it from Twig's counterpart filter).
I don't handle associative arrays for simplicity's sake.
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;
}
})
;
It can be used this way:
<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>
The filter results are cached to avoid the 10 $digest() iterations reached. Aborting!
error.
Update for Angular9:
<div class="container-fluid" *ngFor="let item of alphabet; let index = index">
<div class="row" *ngIf="index % 4 == 0">
<div *ngFor="let i of [index, index+1, index+2, index+3]">
<div class="col-xs-3">{{alphabet[i]}}</div>
</div>
</div>
</div>