4

I have a pretty simple scenerio where a collection of records is available and I need to display them in a simple ng-repeat. However I need the records grouped by a property, and my goal is not not have to alter the collection in order to have this grouping done. My thought is that some type of filter could be applied, but in practice filters, well filter, data and don't group. Is there a way to have a collection and simply group and repeat?

really hack-ish jsFiddle is here with what I'm trying to do.

http://jsfiddle.net/bryangrimes/RufQh/5/

in short the idea is along the lines of:

<ul>
      <li ng-repeat="log in logs grouped by log.dept">
        <h4>{{log.dept}}</h4>
        {{log.name}} worked {{log.hours}} this week
      </li>            
  </ul>

Update: So in the end we were already using taffyDB for housing the original dataset, so that was just expanded.

$.each($scope.logs, function() {
        var log = $(this)[0];

        // build angular friendly data structure
        log.workLogsDB = TAFFY(log.worklogs);
        log.deptLogs   = [];

        $.each(log.workLogsDB().distinct('department').sort(), function() {
            var dept  = $(this)[0].toString();
            var cost  = log.workLogsDB({department:dept}).sum('cost');
            var hours = log.workLogsDB({department:dept}).sum('hours');
            var items = log.workLogsDB({department:dept}).get();

            log.deptLogs.push({
                department: dept,
                total_cost: cost,
                total_hours: hours,
                line_items: items
            });
        });
    });

and the HTML to render:

<div ng-repeat="log in logs">
                <h3 onclick="$('#{{log.project}}').slideDown()">    
                    {{log.project}} hours:{{log.hours}} cost: ${{log.cost}}
                </h3>
                <ul id="{{log.project}}" style="display: none;">
                    <div ng-repeat="log in log.deptLogs">
                        <span style="font-weight: bold; text-transform: capitalize;">{{ log.department }} - Team Cost: ${{ log.total_cost }}, Team Hours: {{ log.total_hours }} </span>
                        <li ng-repeat="line in log.line_items">
                            {{line.employee}} {{line.hours}} hours
                        </li>
                        <br>
                    </div>
                </ul>
            </div>
bryan
  • 1,031
  • 2
  • 17
  • 36

3 Answers3

7

I think you'll need to create a sorted copy of the collection, then ng-repeat over that sorted collection. Here is a fiddle I wrote a while ago when someone else asked a similar question. Salient points:

function MyCtrl($scope, orderByFilter) {  // inject orderByFilter
   $scope.sortedLogs = orderByFilter($scope.logs, '+dept');
}

HTML:

<ul>
  <li ng-repeat="log in sortedLogs">
      <ng-switch on="$first || log.dept != sortedLogs[$index-1].dept">
          <div ng-switch-when="true" class="group"><h4>{{log.dept}}</h4>
      </ng-switch>
      {{log.name}} worked {{log.hours}} this week
  </li>
</ul>
Adam Pearce
  • 9,243
  • 2
  • 38
  • 35
Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492
  • So this is exactly the answer I was looking for to understand how to handle something grouped like this. In the end I think that my actual data structure is a bit complicated for what I want to do, but I'll hack on it today and post what I get. Thanks so much. – bryan Dec 29 '12 at 16:55
1

I like Mark's answer, my tactic is usually to reshuffle the collection in order to make rendering easier.

Here is a fiddle showing that approach http://jsfiddle.net/RufQh/12/

var sorted = [];
logs.forEach(function(log){if (!sorted[log['dept']]) {sorted[log['dept']]= []} sorted[log['dept']].push(log);})

$scope.depts=[];
for(var dept in sorted) {
    $scope.depts.push({name:dept,items:sorted[dept]});
}

Once I have this then I'd render w/ nested loops

<li ng-repeat="dept in depts">{{dept.name}}<br/> 
          <ul>
              <li ng-repeat="log in dept.items">{{log.name}} worked {{log.hours}} this week as a(n) {{log.dept}}</li>
          </ul>
  </li>

Transforming the data is easier with a framework, either map/reduce or something like taffyBD. The downside of this approach is the multiple passes through the data (rather then a single sort) and the added creation of angular scopes.

The upside is that it may be easier to render the content as it is organized in the controller.

aheld
  • 106
  • 3
0

You are already using taffydb for the data so it may not be relevant to you but others who are looking to do this may find ForerunnerDB (http://www.forerunnerdb.com) of use as it has advanced MongoDB-like queries and also supports data views so that you can filter a collection by a query and generate a view from it.

Views contain up-to-date filtered data from the underlying collection and if data on the collection changes the views are automatically updated as well.

Rob Evans
  • 6,750
  • 4
  • 39
  • 56