angularjstaffydb

Grouping collection in an AngualJS ng-repeat?


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>

Solution

  • 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>