Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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>
like image 725
bryan Avatar asked Feb 18 '23 00:02

bryan


2 Answers

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>
like image 149
Mark Rajcok Avatar answered Mar 08 '23 01:03

Mark Rajcok


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.

like image 27
aheld Avatar answered Mar 07 '23 23:03

aheld