2

The goal is to create this

<h3>11.4.2013</h3>
<ul>
 <li>entry 1</li>
 <li>entry 2</li> 
 <li>entry 3</li>
</ul>

<h3>10.4.2013</h3>
<ul>
 <li>entry 4</li>
 <li>entry 5</li> 
 <li>entry 6</li>
</ul>

from this

[
    {
        "name": "entry1",
        "date": "11.4.2013"
    },
    {
        "name": "entry2",
        "date": "11.4.2013"
    },
    {
        "name": "entry3",
        "date": "11.4.2013"
    },
    {
        "name": "entry4",
        "date": "10.4.2013"
    },
    {
        "name": "entry5",
        "date": "10.4.2013"
    },
    {
        "name": "entry6",
        "date": "10.4.2013"
    }
]

The problem is that ng-repeat would have to be on li so I wouldn't never be able to do this using ng-repeat, is that right? I found this http://jsfiddle.net/mrajcok/CvKNc/ example from Mark Rajnoc, but it's still pretty limiting..

What other choices do I have? Write my own ng-repeat like directive? Or is there another way to do it without writting one?

fxck
  • 4,898
  • 8
  • 56
  • 94
  • Lets look at this another way. If you werent using Angular, how would you do this? Is it possible to display it the way you want without transforming the data? – ganaraj Apr 11 '13 at 08:22
  • If I werent using angular, I'd do it like this http://jsfiddle.net/zNW54/ or similar – fxck Apr 11 '13 at 08:42

2 Answers2

7

You could write your own filter that filters out the unique dates for an outer ng-repeat, something like:

filter('unique',function(){
  return function(items,field){
    var ret = [], found={};
    for(var i in items){
      var item = items[i][field];
      if(!found[item]){
        found[item]=true;
        ret.push(items[i]);
      }
    }
    return ret;
  }
});

with the following markup:

<div ng-repeat="dateItem in items | unique:'date' | orderBy:'date'">
<h3>{{dateItem.date}}</h3>
<ul>
  <li ng-repeat="item in items | filter:dateItem.date">
    {{item.name}}
  </li>
</ul>  
</div>

Have a look at this working plnkr example -- or this updated example with adding items

However, if your going to have a lot of items (hundreds or thousands) this solution is not the most optimal. An alternative approach would be to create a more optimal data structure. You can even get this to work with your original data structure by adding a $watch - something like:

$scope.$watch('items',function(){
  var itemDateMap = {};

  for(var i=0; i<$scope.items.length; i++){
    var item = $scope.items[i], key = item.date;
    if(!itemDateMap[key]){
      itemDateMap[key] = [];
    }
    itemDateMap[key].push(item);
  }

  $scope.itemDateMap=itemDateMap;


},true);

Works with this markup:

<div ng-repeat="(date,subItems) in itemDateMap">
<h3>{{date}}</h3>
<ul>
  <li ng-repeat="item in subItems">
    {{item.name}}
  </li>
</ul>  
</div>

Here is an example where you can add lots of random items.

joakimbl
  • 18,081
  • 5
  • 54
  • 53
  • Wait, since when can you use ng-repeat as an element? Anyway how is this performace wise? Wouldn't it be better to to transform the data structure on init like this http://jsfiddle.net/x4sTr/? Also one important thing is though, that I'm gonna be editing and adding new entries on the fly.. – fxck Apr 11 '13 at 09:12
  • adding definitely works http://plnkr.co/edit/Bkgd4V7y3Eg1qhaeyVAN?p=preview I'm still a little bit concerned with performace though.. – fxck Apr 11 '13 at 09:46
  • Sorry, you can't use ng-repeat as an element. But you can use it with any element, so it works - but it's a bad example. I've changed it to a div. Performance wise you should be fine, unless you have a very very large collection. If you don't mind a new data structure you can do a transformation in an init function. But with my approach you don't have to do that, and you can add more items to the original collection easily - [see an updated example](http://plnkr.co/edit/81knnd8KeUdYcMPW9foa?p=preview). – joakimbl Apr 11 '13 at 09:47
  • Ha :) well there's going to be possibly up to.. 400 entries.. each entry will have additional filters on them.. – fxck Apr 11 '13 at 10:11
  • Also that date's also got time, so I won't be able to filter items as easily.. I'm just gonna accept your answer, it's not really gonna help me, but it's probably the right asnwer to what I asked. – fxck Apr 11 '13 at 10:25
  • Ok, performance wise your best sollution would be to change the data structure - but if you want to work with your original data structure you can do [something like this](http://plnkr.co/edit/Mg4he9DJXRxhoXkzOHWH?p=preview) and still have good performance I think - I had no problems with adding thousands of random items – joakimbl Apr 11 '13 at 10:49
  • It should be quite simple to modify both examples to work with time I think – joakimbl Apr 11 '13 at 11:04
1

When I have same needs like yours, I used Object instead of Array.

<div ng-repeat="item in data">
  <h1>{{item.date}}</h1>
  <ul ng-repeat="name in item.names">
    <li>{{name}}</li>
  </ul>
</div>

http://jsfiddle.net/shoma/DqDsE/1/

This question page would help you.

What are the nuances of scope prototypal / prototypical inheritance in AngularJS?

Community
  • 1
  • 1
shoma
  • 618
  • 5
  • 9
  • The problem is you a) can't order objects b) can't easily push() into them. Other than that it's similar to transformation I described here http://stackoverflow.com/questions/15943193/angular-js-more-complex-conditional-loops/15945273?noredirect=1#comment22719317_15944625 but not sure it's gonna work as you edit or add new items(without having to rebuild the whole array/object again).. – fxck Apr 11 '13 at 09:43