first_page

Angular JS 1.x: grouping and sorting JSON-driven sets with Underscore JS

The Angular JS documentation on orderBy surprisingly has all the information I need to sort data. It even shows how the reverse argument can be assigned to a variable. This allowed me to write markup like this:

<div data-ng-repeat="i in groups | orderBy: 'groupName' : vm.indexGroupingSelected.sortDescending ">…</div>

When I use the variable vm, I am conventionally telling myself (because I’m a Microsoft, MVVM guy) that I am using my Angular View Model in Controller Scope ($scope.vm). Since I would like to follow my conventions, it would make sense to have data-ng-repeat="i in vm.groups… " but I’ve found that Angular does not see “dotted” objects in ng-repeat (there may be some 1.x release after 1.2.6 taco-salsafication that fixes this)—so I have no choice but to use $scope.groups.

Now, the angular documentation does not talk about how to fill $scope.groups—to me this is an Underscore thing. I use _.chain() (with pairs() and map()) in $scope.vm.setGroups() to fill $scope.groups:

setGroups: function() {
    $scope.groups = _($scope.vm.data)
        .chain()
        .groupBy(this.indexGroupingSelected.value)
        .pairs()
        .map(function(i) {
            return {
                groupName: i[0],
                group: i[1]
            };
        })
        .value();
}

I need to use pairs() and map()) because the Underscore _.groupBy() function does not return an array; it returns a new object (which is weird to me—but I’m not a JavaScript scientist—see “Underscore.js Grouping in Angular JS” for more details). So, pairs() gives me an array that I map() into an object that most compatible with Angular.

My two code blocks above use $scope.vm.indexGroupingSelected. My use of Selected in the name of this View Model property makes sense when we see this Angular declaration:

<select
    data-ng-change='vm.setGroups()'
    data-ng-model="vm.indexGroupingSelected"
    data-ng-options="i as i.label for i in options">
</select>

Declaring ng-model in a select element binds the currently selected option in $scope.options. Again, I notice that I cannot use $scope.vm.options—I have to use $scope.options. In my Angular Controller, I fill my options like this:

$scope.options = [{
    label: 'by Date',
    sortDescending: true,
    value: 'dateGroup'
}, {
    label: 'by Topic',
    sortDescending: false,
    value: 'topic'
}];

This use of sortDescending in the options is awesome to me. Because the Angular orderBy expression supports not only variables but also “dotting down” objects used as variables my life was made a bit easier. This Angular feature allows me to control sorting data in ascending or descending order with data.

Check out the full CodePen:

Or get the GitHub gist:

My ideas about Angular grouping owe their existence to “Group and Display Data with Underscore and AngularJS” by K. Scott Allen. I wrote a basic grouping CodePen to understand what was going on there. Then, I added the ability to change the grouping with a select element in an ‘intermediate’ grouping CodePen.

https://github.com/BryanWilhite/