2

My angular is 1.0.8-stable

My objective is to display data in rows of 3.

My html should look like

<div class="table-row">
    <div class="item">item1</div>
    <div class="item">item2</div>
    <div class="item">item3</div>
</div>

My pseudo code is if the $index mod 3 == 0 then I will display <div class="table-row">.

if the $index mod 3 == 2, then I will display </div>.

So far, I have this.

<div ng-repeat='item in my_works.items'>
    <!-- if index mod 3 == 0 show <div>-->
    <!-- if index mod 3 == 2 show </div>-->
</div>

Perhaps I was thinking it in the wrong way as there may be a more efficient way to do this in angularjs?

Kim Stacks
  • 10,202
  • 35
  • 151
  • 282
  • first of all, there's `ngIf` , second of all - you're not the first person trying to show elements in rows of 3. Search SO for relevant questions :) – Benjamin Gruenbaum Oct 24 '13 at 11:13
  • @BenjaminGruenbaum Thank you. But I am using the stable 1.0.8 so no ngIf for me. I have searched SO, so far no questions that have the same setup as mine. – Kim Stacks Oct 24 '13 at 11:29

3 Answers3

3

You can use (key, value) in expression – where key and value can be any user defined identifiers, and expression is the scope expression giving the collection to enumerate.

You can try something like this

<div ng-repeat='(index, item) in my_works.items'> 
   <div class="table-row" ng-show="index % 3 == 0">
   </div>
</div>

Reference

Satpal
  • 132,252
  • 13
  • 159
  • 168
  • Note that `ng-show` will hide the entire div (including contents). So you can't use it to create just the opening part of the div. It'd be nice to have this level of control. But sadly we don't- so this answer just hides every third item – KayakDave Oct 24 '13 at 16:40
2

Since you can't use ng-if in 1.0.8 here are two variations that solve the problem. They both wrap 3 items into a table-row div

The outer loop counts off in groups of three, so it fires once per outer table-row div. The second approach uses a filter to control the loop range but it should have better performance. The first approach doesn't require a new filter.

Then the inner ng-repeat loops through the 3 items within a row. It uses slice to get just the 3 array items needed for that particular row.

Here's a fiddle with both of the variations working: http://jsfiddle.net/f8D8q/4/

Option 1: Solution without a new filter:

   <div ng-repeat='itemtmp in items|limitTo:(items.length/3)+1'>
        <div class="table-row"> 
            <span ng-repeat='item in items.slice($index*3,($index*3+3))'>
              {{item.name}} - {{$index}}
            </span>
          </div>
   </div>

Option 2: More performant solution using range filter from http://www.yearofmoo.com/2012/10/more-angularjs-magic-to-supercharge-your-webapp.html#more-about-loops

   <div ng-repeat="n in [] | range:(items.length/3)+1">
        <div class="table-row">
            <span ng-repeat='item in items.slice($index*3,($index*3+3))'>
                {{item.name}} - {{$index}}
            </span>
        </div>
    </div>

and the associated range filter:

 app.filter('range', function () {
    return function (input, total) {
        total = parseInt(total);
        for (var i = 0; i < total; i++) {
            input.push(i);
        }
        return input;
    };
});

Both are tested (down to 1.0.2) and work.

KayakDave
  • 24,636
  • 3
  • 65
  • 68
  • I pick this answer and option 2. This is an excellent answer. I don't understand why it got -1 vote. – Kim Stacks Oct 26 '13 at 06:39
  • I have another issue with sending jsonp requests from angularjs. Care to help? http://stackoverflow.com/q/19603757/80353 – Kim Stacks Oct 27 '13 at 04:51
1
  1. Convert your flat list of items to a list of lists of 3 items.
  2. Iterate over the list of lists.
<div class="table-row" ng-repeat="list in listOfLists">
    <div class="item" ng-repeat="item in list">
      {{ item }}
    </div>
</div>

If you already have a list, items, you could add a filter chunked and replace listOfLists with items|chunked:3, assuming you implement chunked something like this:

app.filter('chunked', function(){
    return function(list, chunkSize){
        var chunks = [];
        angular.forEach(list, function(element, i){
            if(i % chunkSize === 0){
                 currentChunk = [];
                 chunks.push(currentChunk);
            }
            currentChunk.push(element);
        });
        return chunks;
    };
});

I think that matches what you are trying to do; here's a plunker: http://plnkr.co/edit/3h7JprbXFVwnNZErj7hl

I didn't get a chance to test with old Angular though.

Zach Snow
  • 1,014
  • 8
  • 16