Skip to content
Ilya edited this page Sep 5, 2017 · 1 revision

order groupBy result

Question: how to use orderBy with groupBy ?
See: issue #26

$scope.groups = [
    { category: 'alpha', id: 2 },
    { category: 'beta',  id: 3 }, 
    { category: 'gamma', id: 0 },
    { category: 'alpha', id: 4 },
    { category: 'beta',  id: 5 }, 
    { category: 'gamma', id: 1 }
   ];
  // retrieves the min 'id' of a collection, used for the group ordering.
  //you can use lodash instead. e.g: _.min(arr, 'id') 
  $scope.min = function(arr) {
    return $filter('min')
      ($filter('map')(arr, 'id'));
  }
 <ul ng-repeat="group in groups | groupBy:'category' | toArray:true | orderBy:min">
    <!-- print the group name -->
    <li>{{ group.$key }}</li>
    <!-- iterate over the group members and order each group by id -->
    <li ng-repeat="item in group | orderBy:'id'">
      {{ item }}
    </li>
</ul>
<!--RESULT:
- gamma
  - {"category":"gamma","id":0}
  - {"category":"gamma","id":1}
- alpha
  - {"category":"alpha","id":2}
  - {"category":"alpha","id":4}
- beta
  - {"category":"beta","id":3}
  - {"category":"beta","id":5}

withFilter

Question: how to filter (key, value) with ng-repeat ?
See: issue #29 or stackoverflow question

angular.module('app', ['angular.filter'])
  .controller('MainCtrl', function($scope) {
  //your example data
  $scope.items = { 
    'A2F0C7':{ secId:'12345', pos:'a20' },
    'C8B3D1':{ pos:'b10' }
  };
  //more advantage example
  $scope.nestedItems = { 
    'A2F0C7':{
      details: { secId:'12345', pos:'a20' }
    },
    'C8B3D1':{
      details: { pos:'a20' }
    },
    'F5B3R1': { secId:'12345', pos:'a20' }
  };
});
 <b>Example1:</b>
  <p ng-repeat="item in items | toArray: true | pick: 'secId'">
    {{ item.$key }}, {{ item }}
  </p>

  <b>Example2:</b>
  <p ng-repeat="item in nestedItems | toArray: true | pick: 'secId || details.secId'">
    {{ item.$key }}, {{ item }}
  </p> 

Inject filters

Usage: <name>Filter
Example: maxFilter uniqFilter

angular.module('app', ['angular.filter'])
  .controller('MainCtrl', function($scope, maxFilter) {
    $scope.list = [1,23,100,64,-79];
    $scope.max = maxFilter($scope.list);
    //...
  })
  //or
  .factory('SomeFactory', function($filter) {
    var maxFilter = $filter('max');
    //...
  });

Extends filters

You can extends existing filter by using the decorator, see quick example:

angular.module('app', ['angular.filter'])
  .controller('MainCtrl', function($scope) {
    $scope.players = [
      {name: 'Gene', team: 'alpha'},
      {name: 'George', team: 'alpha'},
      {name: 'Steve', team: 'gamma'},
      {name: 'Paula', team: 'alpha'},
      {name: 'Scruath', team: 'gamma'},
      {name: 'Ariel', team: 'beta'},
      {name: 'Mani', team: 'gamma'},
      {name: 'Dan', team: 'beta'},
      {name: 'Jhon', team: 'gamma'}
    ];
  })
  .config(['$provide', function($provide) {
  $provide.decorator('countByFilter', ['$delegate', function($delegate) {
    //get the source filter function
    var srcFilter = $delegate;

    var extendsFilter = function() {
      //srcFilter result
      var srcResult = srcFilter.apply(this, arguments);
      //if there is another arguments, we actually want to return the output
      return !arguments[2] ? srcResult : sortByVal(srcResult);
    };
    
    //get an object and return it sorted
    function sortByVal(obj) {
      var result = {};
      Object.keys(obj)
        .sort(function(b,c) {
                return obj[b] > obj[c] ? 1 : -1;
        })
        .forEach(function(key) {
          result[key] = obj[key];
        });
      return result;
    }
    //we return the extends filter function
    return extendsFilter;
  }]);
}]);
<body ng-app="app" ng-controller="MainCtrl">
  <p>{{ players | countBy: 'team' }}</p>
  <b>Sorted list:</b>
  <p>{{ players | countBy: 'team': true }}</p>
</body>