jnthnjns
jnthnjns

Reputation: 8925

Horizontal ng-repeat(er) with new row breaks

Working with Bootstrap and AngularJS, is there a way to ng-repeat horizontally with a new row for every set amount of elements?

I have been playing around with ng-class to accomplish this Fiddle, but the problem is that I can't get the float left divs within the initial row div... Any thoughts, am I not thinking of something or would this best be done with a Directive?

Here is my code (live example in the above fiddle link):

<body ng-app="myApp">
    <div ng-controller="MyCtrl">
        <div ng-repeat="num in numbers" 
            ng-class="{'row': ($index)%2==0, 'col-md-6': ($index)%2!=0}">
            <div ng-class="{'col-md-6': ($index)%2==0}">
                {{num}}
            </div>
        </div>
    </div>
</body>
var myApp = angular.module('myApp', [])
    .controller('MyCtrl', function ($scope) {
        $scope.numbers = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"];
});
.row {
    clear: both;
    width: 100%;
    border: 1px solid red;
}
.col-md-6 {
    width: 50%;
    float: left;
    border: 1px solid blue;
}

Upvotes: 13

Views: 37051

Answers (8)

Josip
Josip

Reputation: 1110

To keep solution bootstrap formated i solved this using 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>

Upvotes: 0

Marshee
Marshee

Reputation: 23

My approach was to use the $index variable, which is created and updated by AngularJS within an ng-repeat directive, to trigger a call to the CSS clearfix hack which, in turn, resets to a new row.

I am using the following versions: AngularJS 1.5.5 and Bootstrap 3.3.4

<!-- use bootstrap's grid structure to create a row -->
<div class="row">

    <!-- Cycle through a list: -->
    <!-- use angular's ng-repeat directive -->
    <div ng-repeat="item in itemList">

        <!-- Start a new row: -->
        <!-- use the css clearfix hack to reset the row -->
        <!-- for every item $index divisible by 3. -->
        <!-- note that $index starts at 0. -->
        <div ng-if="$index % 3 == 0" class="clearfix"></div>

        <!-- Create a column: -->
        <!-- since we want 3 "item columns"/row, -->
        <!-- each "item column" corresponds to 4 "Bootstrap columns" -->
        <!-- in Bootstrap's 12-column/row system -->
        <div class="col-sm-4">
            {{item.name}}
        </div>
    </div>
</div>

Upvotes: 1

Suresh Bala
Suresh Bala

Reputation: 2362

Depending upon the number of columns that you need in your template, create chunks of the original data source in your controller.

$scope.categories = data //contains your original data source;
$scope.chunkedCategories = [] //will push chunked data into this;
//dividing into chunks of 3 for col-4. You can change this
while ($scope.categories.length > 0)
 $scope.chunkedCategories.push($scope.categories.splice(0, 3));

In your template you can now do the following

<div class="row" ng-repeat="categories in chunkedCategories">
 <div class="col-xs-4" ng-repeat="category in categories">
  <h2>{{category.title}}</h2>
 </div>         
</div>  

Upvotes: 1

Saud Alfadhli
Saud Alfadhli

Reputation: 856

No need to add .row class .. I did this:

HTML:

<div ng-repeat="product in allProducts">
  <div class="my-col-50">
    <h1>{{product.title}}</h1>
  </div>
</div>

CSS:

.my-col-50{float:left;width:50%;}

and it's work like a charm.

Upvotes: 3

Kcats Wolfrevo
Kcats Wolfrevo

Reputation: 813

I tried two of the suggestions given here... the one by yshaool works fine but like i commented on it give me that infinite loop error.

Then I tried something like below:

<div class="row" ng-repeat="row in split($index, 3, row.Attempts)">
      <div class="col-md-4" ng-repeat="attempt in row">
          <div>Attempt {{row.AttemptNumber}}</div>
          <div>{{row.Result}}</div>
      </div>              
</div>

and the function:

$scope.split = function (index, length, attempts) {

       var ret = attempts.slice(index * length, (index * length) + length);
       console.log(JSON.stringify(ret));
       return ret;

}

was going somewhere with that when i realized that it could be as simple as

<div ng-repeat="attempt in row.Attempts">
      <div class="col-md-4">
          <div>Attempt {{attempt.AttemptNumber}}</div>
          <div>{{attempt.Result}}</div>
     </div>                    
</div>

using "col-md-4" does the trick as I only need to split using three columns per row.. (let bootstrap do the work!) anyway the other answers here were really useful...

Upvotes: 2

Jonathan Hamilton
Jonathan Hamilton

Reputation: 56

Although this isn't the "proper" way of doing this, there is a way to achieve this using CSS.

For example, this is for a 3 column layout:

HTML:

<div class="row">
    <div ng-repeat="(key, pod) in stats.pods" class="pod-wrap">
        <div ng-if="objectCheck(pod) == false" class="col-md-4 col-sm-4 pod">
            <div>
                <h2 ng-bind="key"></h2>
                <p class="total" ng-bind="pod | number"></p>
            </div>
        </div>

        <div ng-if="objectCheck(pod) == true" class="col-md-4 col-sm-4 pod">
            <div>
                <h2 ng-bind="key"></h2>

                <div ng-repeat="(type, value) in pod track by $index">
                    <p class="status"><% type %> <small><% value %></small></p>
                </div>
            </div>
        </div>
    </div>
</div>

CSS:

.pod-wrap:nth-of-type(3n):after {
    display: table;
    content: '';
    clear: both;
}

Upvotes: 2

yshaool
yshaool

Reputation: 103

If you are working with Bootstrap 3 and AngularJS you can declare a new filter that will return one array of sub array slices and then do two ng-repeat.

It will look like that:

  <div class="row" ng-repeat="row in filtered = (arr | splitArrayFilter:3)">
        <div class="col-md-4" ng-repeat="n in row">
          <h3>{{n}}</h3>
        </div>
  </div>
app.filter('splitArrayFilter', function() {
  return function(arr, lengthofsublist) {
    if (!angular.isUndefined(arr) && arr.length > 0) {
      var arrayToReturn = [];  
      var subArray=[]; 
      var pushed=true;      
      for (var i=0; i<arr.length; i++){
        if ((i+1)%lengthofsublist==0) {
          subArray.push(arr[i]);
          arrayToReturn.push(subArray);
          subArray=[];
          pushed=true;
        } else {
          subArray.push(arr[i]);
          pushed=false;
        }
      }
      if (!pushed)
        arrayToReturn.push(subArray);

      console.log(JSON.stringify(arrayToReturn));
      return arrayToReturn; 
    }
  }
}); 

You can Find it on Plunker here: http://plnkr.co/edit/rdyjRtZhzHjWiWDJ8FKJ?p=preview for some reason the view in plunker does not support bootstrap 3 columns but if you open it in embedded view or in browsers you can see that it works.

Upvotes: 9

EnigmaRM
EnigmaRM

Reputation: 7632

It was clever what you were doing with ng-class. I hadn't ever thought of using %2 within the expression there.

But for future reference, there is a slightly easier way to accomplish that: ng-class-even and ng-class-odd. It does the same thing as what you were doing, but just a bit cleaner:

<div ng-repeat="num in numbers" ng-class-even="'md-col-6'" ng-class-odd="'row'">
    {{num}}
</div>

But this doesn't resolve your problem. If I understand you correctly, you want a row, with two columns within that row. The easiest way I could think of is to split up the arrays. Put the repeat on the div, then have 2 span within the div. I think one of the issues that you had originally, is that you were repeating a single div, and trying to treat that block element as an inline

Controller

myApp.controller('MyCtrl', function ($scope) {
    $scope.evens = ["2","4","6","8","10","12","14"];
    $scope.odds = ["1","3","5","7","9","11","13"];
});

HTML

<div ng-controller="MyCtrl">
    <div ng-repeat="odd in odds" class="row">
        <span class="span3">{{odd}}</span>
        <span class="span2">{{evens[$index]}}</span>
    </div>
</div>

Fiddle

Being that you're using version 1.1.5, that also opens you up to a new directive: ng-if! You could also use ng-switch to do some conditional logic displays.

You didn't include bootstrap in your fiddle, and for some reason I can't get jsFiddle to display bootstrap. So I created some temp CSS classes that would somewhat resemble bootstraps class="span"

Upvotes: 6

Related Questions