pvogel
pvogel

Reputation: 121

add bootstrap rows during ng-repeat

I have a situation where I have a list of data to be displayed in individual panels, Using Bootstrap's grid system, I'd like to take advantage of a wide screen and display several panels horizontally, but on narrow screens have them stack. I'm currently laying things out on the server side with ejs like this, with columns being passed in as a query parameter, typically set to 2 or 3, so each colClass is either col-sm-6 or col-sm-4.

<% var colWidth = 12/columns; var colClass = "col-sm-" + colWidth; %>
<% for(var i=0; i<contestData.classData.length; i++) {%>
    <% if ((classCount % columns) == 0) { %>
        <div class="row">
    <% } %>
    <div class="<%= colClass %>">
        <div class="panel panel-primary">
            <div class="panel-heading">
                <h3 class="panel-title"> <%= contestData.classData[i].name %> </h3>
            </div>
            <div>...</div>
        </div>
    </div>
    <% classCount++ %>
    <% if ((classCount % columns) == 0) { %>
        </div>
    <% } %>
<% } %>

This works, but doing this level of layout on the server side offends me, I'd really rather do this with Angular but I can't figure out how to wrap the appropriate number of panels in a div with class=row while doing ng-repeat or even ng-repeat-start="classData in contestData.classData"

Thanks!

Upvotes: 11

Views: 16755

Answers (5)

You can add something like this, first in your controller, do a function dad gets an integer "breakpoint" that is the number of columns you want to wrapped by a row, and the data you want inside each column like so:

  function getRows(breakpoint,data) {
        var len = data.length; var i = 0;
        var rows = []; var temp = [];
        for (; i < len; i++) {
            if (i % breakpoint == 0 && i != 0) {
                rows.push(temp);               
                temp = [];
            } 
            temp.push(data[i]);
        }
        var len2 = rows.length * breakpoint;
        if (len > len2) {
            //var leftOvers = len - len2;
            i = len2; temp = [];
            for (; i < len; i++) {
                temp.push(data[i]);
            }
            rows.push(temp);
        }

        return rows;
    }

then whenever you recive the data yo simply do:

 $scope.rows = getRows(3,data); // in case you want 3 cols.

then in your html:

    <div class="row"  ng-repeat="row in rows">
     <div class="col-lg-4" ng-repeat="data in row">
       {{data.whatever}}
     </div>
</div>
     </div>

and that`s it, it should work for u.

Upvotes: 0

Oswaldo Alvarez
Oswaldo Alvarez

Reputation: 4812

Here a simple solution with just HTML, 3 ROWS

<div class="row" >
    <div class="col-md-4" ng-repeat-start="item in data">
        I'M A ROW
    </div>
    <div class="clearfix" ng-if="($index+1)%3==0"></div>
    <div ng-repeat-end=""></div>
</div>

Upvotes: 55

Andrii Hidden
Andrii Hidden

Reputation: 51

I have this decision, seems to be working for 3 col

<div ng-repeat="r in data">
    <div class="row" ng-if="$index%3==0">
        <div class="col-md-4" ng-if="$index<data.length">
        {{data[$index]}}
        rrr
        </div>
        <div class="col-md-4" ng-if="$index+1<data.length">
        {{data[$index+1]}}
        rrr
        </div>
        <div class="col-md-4" ng-if="$index+2<data.length">
        {{data[$index+2]}}
        rrr
        </div>
    </div>
</div>

and data is

$scope.data = ['1','2','3','4','5','6','7'];

Upvotes: 1

pvogel
pvogel

Reputation: 121

Answering my own question here, similar to the answer from j.wittwer, I created a filter to chunk my data appropriately by row, etc.:

angular.module('myApp.filters').
    filter('rowfilter', function () {
        return function (data, columnCount) {
            var rows = [];
        var colCount = columnCount || 2;
        var columns = [];
        for (var i = 0; i< data.length; i++) {
        columns.push(data[i]);
        if (columns.length == colCount) {
        rows.push(columns);
        columns = [];
        }
        }
        if (columns.length > 0) {
        rows.push(columns);
        }
        return rows;
        };
    });

And then I use the filter (jade shown here): .row(ng-repeat="row in contestData.classData | rowfilter") .col-sm-6(ng-repeat="column in row")

Works very nicely, still wrapping my head around Angular!

Upvotes: 1

j.wittwer
j.wittwer

Reputation: 9497

If you start by chunking your data into smaller parts, based on the number of columns, it will be easy to use nested ng-repeats to create your layout:

$scope.getRows = function(array, columns) {
  var rows = [];

  //https://stackoverflow.com/questions/8495687/split-array-into-chunks
  var i,j,temparray, chunk = columns;
  for (i=0,j=array.length; i<j; i+=chunk) {
      temparray = array.slice(i, i+chunk);

      rows.push(temparray);
  }

  return rows;
};

$scope.rows = $scope.getRows($scope.contestData, $scope.columns);

Then your markup is simply:

<div ng-repeat="row in rows">
  <div class="row">
    <div ng-class="{'col-xs-4': columns == 3, 'col-xs-3': columns == 4}" ng-repeat="contest in row">
      <div class="panel panel-primary">
          <div class="panel-heading">
            <h3 class="panel-title">{{contest}}</div>
          </div>
      </div>
    </div>
  </div>
</div>

Notice that ng-class is doing the work of deciding which type of class to add based on the number of columns. This example is handing 3 and 4, but you could extend it to handle others.

Here is a working demo: http://plnkr.co/edit/B3VAXlq9dkzO3hQkbkN3?p=preview

Update:
Plunker's full screen mode seems to interfere with the column width style, so I changed the link to display in preview mode.

Upvotes: 4

Related Questions