2

Still this problem Angular.js more complex conditional loops but I felt that the answer to the question as it was asked was right so I accepted it.

So let me elaborate more than I did in the original question.

I'm trying to get this

<h3>11.4.2013</h3>
<ul>
 <li>oofrab | 4 | 11.4.2013 14:55 <button>remove</button></li>
 <li>raboof | 3 | 11.4.2013 13:35 <button>remove</button></li>
</ul>

<h3>10.4.2013</h3>
<ul>
 <li>barfoo | 2 | 10.4.2013 18:10 <button>remove</button></li>
 <li>foobar | 1 | 10.4.2013 12:55 <button>remove</button></li> 
</ul>

from this data structure

[
    {
        "id": 4,
        "name": "oofrab",
        "date": "2013-11-04 14:55:00"
    },
    {
        "id": 3,
        "name": "raboof",
        "date": "2013-11-04 13:55:00"
    },
    {
        "id": 2,
        "name": "barfoo",
        "date": "2013-10-04 18:10:00"
    },
    {
        "id": 1,
        "name": "foobar",
        "date": "2013-10-04 12:55:00"
    }
]

Basically the only extra thing over the standard ng-repeat I want to add are those headings. And I simply can't believe I'd have to go thru so many problems by adding them.

This is what I ended up with using the answer I got in the first question http://plnkr.co/edit/Zl5EcsiXXV92d3VH9Hqk?p=preview

Note that there can realistically be up to 400 entries. And I need to be able to add/remove/edit entries on the fly

What the example on plunker is doing is this:

iterating thru the original data creating a new data structure looking like this

{
  "2013-10-05": [
    {
      "id": 4,
      "name": "oofrab",
      "date": "2013-10-05 14:55:00",
      "_orig_index": 0
    },
    {
      "id": 3,
      "name": "raboof",
      "date": "2013-10-05 13:55:00",
      "_orig_index": 1
    }
  ],
  "2013-10-04": [
    {
      "id": 2,
      "name": "barfoo",
      "date": "2013-10-04 18:10:00",
      "_orig_index": 2
    },
    {
      "id": 1,
      "name": "foobar",
      "date": "2013-10-04 12:55:00",
      "_orig_index": 3
    }
  ]
}

allowing me to then get the result I wanted by doing this

<div ng-repeat="(date,subItems) in itemDateMap">
<h3>{{date}}</h3>
<ul>
  <li ng-repeat="item in subItems">
    {{item.name}} | {{item.id}} | {{item.date}}
    <button type="button" ng-click="removeItem(item._orig_index)">x</button>
  </li>
</ul>  
</div>

Great. But it comes with a cost of shizzload of problems. Everytime a new item is added I have to rebuild the itemDateMap, everytime an item is deleted I have to rebuild the itemDateMap, everytime date is changed, I have to rebuild the itemDateMap. When I want to remove an item, I have to first get index of its original reference. And everytime itemDateMap is rebuilt, the whole thing is re-rendered. And it can't be sorted, as it's an object rather than an array.

When there's a couple of hundred of entries, it also becomes really, really slow. I read somewhere that ng-repeat is quite intelligent, watching values, moving nods in dom rather than re-rendering everything and stuff, but it surely doesn't work this way when I rebuild the whole structure.

This can't be right, all this hassle to do a very, very simple thing..

What should I do?

Community
  • 1
  • 1
fxck
  • 4,898
  • 8
  • 56
  • 94
  • you can improve the performance by editing the map instead of creating it every time – Arun P Johny Apr 12 '13 at 07:31
  • how would I do that given that the new entry may be added to the middle of the array.. I'd have to loop thru itemDateMap, find if there's day which == with the date of the new entry, if not, add it to the according position, if the day is already there, loop thru its items and add it to the according position.. damn that's still alot to do a simple grouping of items.. also as it is right now, itemDateMap is an object, so I can't quite edit it easily.. – fxck Apr 12 '13 at 07:46
  • @foxx have you thought about creating multiple objects (like for every day) instead of one big one? I could imagine that this improves the performance if you have 400 entries (or more) as only the ng-repeat for a day has to be rebuild on edits. – F Lekschas Apr 12 '13 at 09:46
  • Well the data structure you see is what I get from the server and it's not gonna change. But I guess what you are saying is basically what's happening anyway, that big object is split to days on init, with entries for each day in it. – fxck Apr 12 '13 at 09:53

1 Answers1

3

This is my suggestion - just work with one structure, and only expose one structure to the scope (the map). And create a function to add an array of items to the map, and a function that transforms the map into an array (I assume you need this array for server communication or something).

  var toKey=function(item){
    return moment(item.date).format("YYYY-MM-DD");
  }

  $scope.itemDateMap = {};
  $scope.addItemToDateMap=function(item){
    var key = toKey(item);
    if(!$scope.itemDateMap[key]){
      $scope.itemDateMap[key] = [];
    }
    $scope.itemDateMap[key].push(item);    
  }

  $scope.removeItemFromDateMap=function(item){
    var key = toKey(item), subitems = $scope.itemDateMap[key];
    var index = subitems.indexOf(item);
    subitems.splice(index,1);
    if(subitems.length === 0){
      delete $scope.itemDateMap[key];
    }
  }

  var addArrayToMap = function(items){
    for(var i=0; i<items.length; i++){
      var item = items[i]; 
      $scope.addItemToDateMap(item);
    }
  };

  $scope.mapToArray = function(){
    var items = [];
    for(var key in $scope.itemDateMap){
      var subitems = $scope.itemDateMap[key];
      for(var j=0;j<subitems.length;j++){
        var item = subitems[j];
        items.push(item);
      }
    }
    return items;
  }

I've updated your plnkr with my suggestion. I think it performs quite well.

Oh - I just noticed you want it sorted - I don't have time to update my example, but it is not very complicated. Use this structure instead (array with objects with arrays, instead of object with array) - this way you can use the orderBy:'date' on the root array:

[
{
  date:"2013-10-05",
  items: [
    {
      "id": 4,
      "name": "oofrab",
      "date": "2013-10-05 14:55:00"
    },
    {
      "id": 3,
      "name": "raboof",
      "date": "2013-10-05 13:55:00"
    }
  ]
},
{
date:"2013-10-04",
items: [
    {
      "id": 2,
      "name": "barfoo",
      "date": "2013-10-04 18:10:00"
    },
    {
      "id": 1,
      "name": "foobar",
      "date": "2013-10-04 12:55:00"
    }
  ]
}
]
joakimbl
  • 18,081
  • 5
  • 54
  • 53
  • Looks good, it is definitely little faster than before. There still one damn problem it's not gonna solve and that is changing date on the item, which should move the item / create new day.. – fxck Apr 12 '13 at 10:35
  • Which is also why I had that onblur directive there. But I solved this by using your remove and add functions – fxck Apr 12 '13 at 10:45
  • "array with objects with arrays" would break all the functions you made though.. wouldn't it? – fxck Apr 12 '13 at 12:10
  • Ah, I see, I forgot about reordering after date change, sorry. Using add/remove seems like a reasonable approach. And yes, you would have to make some modifications - I don't have time to do it right now, but they shouldn't be too complicated. – joakimbl Apr 12 '13 at 12:57
  • Changed it in the end a little bit, but it definitely helped me, thanks. It's still pretty sad you have to do so much just to be able to group things. – fxck Apr 13 '13 at 10:03