Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change step size for ngRepeat

Tags:

html

angularjs

I have an existing HTML template that places tiles on the page using ngRepeat,

<div ng-repeat="product in productList">
    <div class="product-tile">
       Product content goes here...
    </div>
</div>

The designer now needs to wrap every 3 tiles in an extra div.

Normally, I would loop through the list and either,

  • Step each loop by 3 items with 3 tiles wrapped in the div in a single iteration, testing to see if the 2nd and 3rd elements exist (for when the list length is not a multiple of 3)
  • Or step through the list one at a time as normal and check the index of the item. If a mod of 3 equalled 0, I would then add the extra div tags

But I can't see how I would do either in Angular. Any suggestions as to how to handle this?

like image 709
richard Avatar asked May 07 '15 01:05

richard


2 Answers

It is much better to "prime" the ViewModel so that it suits your View - it's called View-Model for a reason. So, your modified productList could be:

$scope.productList = 
  [
    [ {/* product */ }, { }, { } ],
    [ { }, { }, { } ],
    // etc...
  ];

And so, to iterate you would nest ng-repeats:

<div ng-repeat="row in productList">
  <div ng-repeat="product in row" class="product-row-group">
    <div class="product-tile">
    </div>
  </div>
</div>

But it is possible (although, inefficient) to "pseudo-step" through the array:

<div ng-repeat="product in productList">
  <div ng-if="$index % 3 === 0" 
       ng-init="group = productList.slice($index, $index + 3)">
    <div class="products-row">
      <div class="product-tile" ng-repeat="grItem in group">
        {{grItem}}
      </div>
    </div>
  </div>
</div>

The first ng-repeat goes over the entire array, but the inner ng-if only renders every 3rd item and ng-init creates a sub-array of 3 products aliased as group. The inner ng-repeat goes over the items in the group of 3 products.

Demo

like image 83
New Dev Avatar answered Nov 05 '22 21:11

New Dev


The only thing I can think to do is to partition the original productList array once when you get it into an array of arrays, and then use nested ng-repeats to get the structure you are after.

The following demonstrates that approach:

angular.module('stackoverflow', []).controller('ProductListsCtrl', function($scope) {
  $scope.productLists = partition([1, 2, 3, 4, 5, 6, 7, 8], 3);
});

/* You could put this in a utility library or something */
function partition(coll, size) {
  var groups = [];
  for (var groupIndex = 0, numGroups = Math.ceil(coll.length / size);
       groupIndex < numGroups;
       groupIndex++) {
    var startIndex = groupIndex * size;
    groups.push(coll.slice(startIndex, startIndex + size));
  }
  return groups;
}
.product-tile {
  display: inline-block;
  background-color: #9A9EDA;
  height: 100px;
  width: 100px;
  line-height: 100px;
  vertical-align: middle;
  text-align: center;
  margin-right: 10px;
}
.wrapper {
  margin: 5px;
  padding: 10px;
  background-color: #634AFF;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="stackoverflow" ng-controller="ProductListsCtrl">
  <div ng-repeat="productList in productLists" class="wrapper">
    <div class="product-tile" ng-repeat="product in productList">
      {{product}}
    </div>
  </div>
</div>
like image 3
GregL Avatar answered Nov 05 '22 23:11

GregL