4

I need to take records with null values on top , when I'm sorting by ASC

<tr ng-repeat="footballer in footballers=(footballers | orderBy:predicate)">
predicate : ['team.name','id]

Some footballers have no team, so team object == null and team.name==null, and I need to have them on the top

I wanted to rewrite sort function, but I need to save predicate

Mel
  • 5,837
  • 10
  • 37
  • 42
rom16rus
  • 45
  • 1
  • 2
  • 8

4 Answers4

8

You can use something like this in your controller:

$scope.nullsToTop = function(obj) {
  return (angular.isDefined(obj.team) ? 0 : -1);
};

And on the HTML:

<tr ng-repeat="footballer in footballers | orderBy:[nullsToTop].concat(predicate)">

This way you can maintain your predicate separately. Just concat the nullsToTop function in the orderBy expression to run it first.

Plunker

bmleite
  • 26,850
  • 4
  • 71
  • 46
1

Create a function in the controller that will receive as parameter the entity and will return either ['team.name','id] either [] or other values that will help push the non-sortable elements to top/bottom of the list.

EDIT (example)

HTML:

<li ng-repeat="item in items | orderBy:getItemOrder track by $index">...

AngularJS Ctrl:

$scope.getItemOrder = function (entity) {
  if (entity.id === null || .... === null) {
    return 0;
  }

  return ['team.name','id];
}
Dragos Rusu
  • 1,508
  • 14
  • 14
  • can you write the example of this function? what i need to do? i tried to rewrite sort function like this : $scope.nullSortFunction = function () { return function (footballer) { if (footballer.team === null) { footballer.team = {}; footballer.team.name = ''; } } }; – rom16rus Sep 04 '14 at 10:21
1

I was able to do it by returning a '+' or a '-' if the value is null, close to what bmleite did but is more contained in the function itself.

1. create a function that adds a + or a - to the null values depending on the sort order (asc or desc) so that they'd always remain at either the top or bottom

    function compareAndExcludeNull(element){
        var compareWith = $scope.order.propertyName; // in your case that's 'predicate'

        var operatorToBottom = $scope.order.reverse ? '+' : '-'; // If decending then consider the null values as smallest, if ascending then consider them biggest
        var operatorToTop = $scope.order.reverse ? '-' : '+';

        if(element[compareWith] == null){
            return operatorToTop;
        } else {
            return operatorToBottom+element[compareWith];
        }
    }

if you want to have the null values at the bottom of the sorted array then just switch the operatorToTop with operatorToBottom and vice versa to get:

    function compareAndExcludeNull(element){
        var compareWith = $scope.order.propertyName; // in your case that's '

        var operatorToBottom = $scope.order.reverse ? '+' : '-'; // If decending then consider the null values as smallest, if ascending then consider them biggest
        var operatorToTop = $scope.order.reverse ? '-' : '+';

        if(element[compareWith] == null){
            return operatorToBottom;
        } else {
            return operatorToTop+element[compareWith];
        }
    }

2. Call the function and pass the array you're sorting

Javascript:

In my case I am calling this function from HTML and passing different propertyName depending on the column the user is sorting by, in your case you'd just need to pass 'predicate' as the propertyName

    $scope.order.sortBy = function(propertyName){
        $scope.order.reverse = (propertyName !== null && $scope.order.propertyName === propertyName) ? !$scope.order.reverse : false;
        $scope.order.propertyName = propertyName;
        $scope.resources = $filter('orderBy')($scope.resources,compareAndExcludeNull,$scope.order.reverse);
    };

HTML:

<div>
    <span ng-if="showKeywords" class="label label-info" ng-click="order.sortBy('keyword')" style="margin-right: 5px">
        Order by Keywords
        <span ng-if="order.propertyName=='keyword'" class="glyphicon glyphicon-triangle-{{order.reverse? 'bottom' : 'top'}}">
        </span>
    </span>
    <span ng-if="showWebsites" class="label label-info" ng-click="order.sortBy('res_title')">
        Order by Website
        <span ng-if="order.propertyName=='res_title'" class="glyphicon glyphicon-triangle-{{order.reverse? 'bottom' : 'top'}}">
        </span>
    </span>
</div>
Naguib Ihab
  • 4,259
  • 7
  • 44
  • 80
1

I know this is an old post but there is a better way for doing this and could not see it anywhere.

var myApp = angular.module('myApp', []);

myApp.controller('myCtrl', ["$scope", function($scope){
  $scope.footballers = [
        {id: 0, qt: 13},
        {id: 1, qt: 2},
        {id: 2, qt: 124},
        {id: 3, qt: 12125},
        {id: null , qt: 122},
        {id: 4, qt: -124},
        {id: 5, qt: -5235},
        {id: 6, qt: 43},
        ]
}]) 
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular.min.js"></script>


<div ng-app="myApp" ng-controller="myCtrl">
<div ng-repeat="footballer in footballers | orderBy:['id==null || id']">
    {{footballer.id}}, {{footballer.qt}}
</div>
</div>

One can also use this to show 'id' with any specific value on top of others

Corrot
  • 241
  • 4
  • 3