1

I have a list with items, and as you can see in the following plunkr, I'm trying to sort the table, using standard orderBy filter:

<tr ng-repeat="item in list | orderBy: sorting.field : sorting.reverse">
     <!-- some code goes here -->
</tr>

See the plunker or snippet below:

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

app.controller('MainCtrl', function($scope) {

  $scope.list = [{
    id: 1,
    title: 'test 1',
    reactions: {
      'like': 7,
      'share': 20,
      'comment': 100
    }
  }, {
    id: 2,
    title: 'test 2',
    reactions: {
      'like': 5,
      'share': 10,
    }
  }, {
    id: 3,
    title: 'test 3',
    reactions: {
      'like': 4,
      'share': 1,
    }
  }, {
    id: 4,
    title: 'test 4',
    reactions: {
      'like': 4,
      'share': 1,
      'comment': 2
    }
  }];

  $scope.sorting = {
    field: 'id',
    reverse: false
  };


});
<!DOCTYPE html>
<html ng-app="plunker">

<head>
  <meta charset="utf-8" />
  <title>AngularJS Plunker</title>
  <script data-require="angular.js@1.3.x" src="https://code.angularjs.org/1.3.5/angular.js" data-semver="1.3.5"></script>
  <script src="app.js"></script>
</head>

<body ng-controller="MainCtrl">
  <div>
    <table>
      <tr>
        <th ng-click="sorting.field = 'id'; sorting.reverse = ! sorting.reverse;">ID</th>
        <th ng-click="sorting.field= 'title'; sorting.reverse = ! sorting.reverse;">Title</th>
        <th ng-click="sorting.field = 'reactions.like'; sorting.reverse = ! sorting.reverse;">Likes</th>
        <th ng-click="sorting.field = 'reactions.share'; sorting.reverse = ! sorting.reverse;">Shares</th>
        <th ng-click="sorting.field = 'reactions.comment'; sorting.reverse = ! sorting.reverse;">Comments</th>
      </tr>
      <tr ng-repeat="item in list | orderBy: sorting.field : sorting.reverse">
        <td>
          {{item.id}}
        </td>
        <td>
          {{item.title}}
        </td>
        <td>
          {{item.reactions.like}}
        </td>
        <td>
          {{item.reactions.share}}
        </td>
        <td>
          {{item.reactions.comment}}
        </td>
      </tr>
    </table>
    <div class="hint">
      <p>* Click on table heading in order to sort results.</p>
    </div>
  </div>
</body>

</html>

The problem comes when some properties are missing (n.b. - can't handle this, as the data comes from external API), take a look at "Comments" column and you'll see what I'm talking about. The data is not sorted as expected.

Yes, I tried everything from this thread, but nothing seems to work for me.

Community
  • 1
  • 1
Kristian Vitozev
  • 5,791
  • 6
  • 36
  • 56
  • What do you expect? If there are *no* comments, should it should be considered as `0` and show up at the beginning when sorted asc? Why can't you *fix* these values before assigning them to the scope? In coffee it would be `item.comments ?= 0 for item in items`. – fracz Jun 13 '16 at 09:25

3 Answers3

1

Instead of hacking with sorting (you can apply any sort function you want) you should fix the data before displaying them.

var possibleReactions = ['like', 'share', 'comment'];
for (var i = 0; i < $scope.list.length; i++) {
  var item = $scope.list[i];
  for (var j = 0; j < possibleReactions.length; j++) {
    var reaction = possibleReactions[j];
    if(!item.reactions[reaction]) item.reactions[reaction] = 0;
  }
}

Check out the working snippet below.

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

app.controller('MainCtrl', function($scope) {

  $scope.list = [{
    id: 1,
    title: 'test 1',
    reactions: {
      'like': 7,
      'share': 20,
      'comment': 100
    }
  }, {
    id: 2,
    title: 'test 2',
    reactions: {
      'like': 5,
      'share': 10,
    }
  }, {
    id: 3,
    title: 'test 3',
    reactions: {
      'like': 4,
      'share': 1,
    }
  }, {
    id: 4,
    title: 'test 4',
    reactions: {
      'like': 4,
      'share': 1,
      'comment': 2
    }
  }];
  
  var possibleReactions = ['like', 'share', 'comment'];
  for (var i = 0; i < $scope.list.length; i++) {
    var item = $scope.list[i];
    for (var j = 0; j < possibleReactions.length; j++) {
      var reaction = possibleReactions[j];
      if(!item.reactions[reaction]) item.reactions[reaction] = 0;
    }
  }

  $scope.sorting = {
    field: 'id',
    reverse: false
  };


});
<!DOCTYPE html>
<html ng-app="plunker">

<head>
  <meta charset="utf-8" />
  <title>AngularJS Plunker</title>
  <script data-require="angular.js@1.3.x" src="https://code.angularjs.org/1.3.5/angular.js" data-semver="1.3.5"></script>
  <script src="app.js"></script>
</head>

<body ng-controller="MainCtrl">
  <div>
    <table>
      <tr>
        <th ng-click="sorting.field = 'id'; sorting.reverse = ! sorting.reverse;">ID</th>
        <th ng-click="sorting.field= 'title'; sorting.reverse = ! sorting.reverse;">Title</th>
        <th ng-click="sorting.field = 'reactions.like'; sorting.reverse = ! sorting.reverse;">Likes</th>
        <th ng-click="sorting.field = 'reactions.share'; sorting.reverse = ! sorting.reverse;">Shares</th>
        <th ng-click="sorting.field = 'reactions.comment'; sorting.reverse = ! sorting.reverse;">Comments</th>
      </tr>
      <tr ng-repeat="item in list | orderBy: sorting.field : sorting.reverse">
        <td>
          {{item.id}}
        </td>
        <td>
          {{item.title}}
        </td>
        <td>
          {{item.reactions.like}}
        </td>
        <td>
          {{item.reactions.share}}
        </td>
        <td>
          {{item.reactions.comment}}
        </td>
      </tr>
    </table>
    <div class="hint">
      <p>* Click on table heading in order to sort results.</p>
    </div>
  </div>
</body>

</html>
Community
  • 1
  • 1
fracz
  • 20,536
  • 18
  • 103
  • 149
0

Most common thing you can do is following

<td>
 <span ng-if="item.reactions.comment">{{item.reactions.comment}}</span>
 <span ng-if="!item.reactions.comment">Zero</span>
</td>

If you find a more precise way then update here.

Rakesh Chand
  • 3,105
  • 1
  • 20
  • 41
  • This does not solve the problem of sorting by this value. Also, it would be much easier like that `{{ item.reactions.comments || 0 }}`. – fracz Jun 13 '16 at 09:32
0

create your own filter

angular
.module("app", [])
.controller("c", function ($scope) {
    $scope.items = [
        {id: 1, name: ""},
        {id: 2, name: "a"},
        {id: 3, name: "b"},
        {id: 4, name: "c"},
        {id: 5, name: ""},
        {id: 6, name: "f"},
        {id: 7, name: ""},
        {id: 8, name: "z"},
    ];
})
.filter("emptyToEnd", function () {
    return function (array, key) {
        var present = array.filter(function (item) {
            return item[key];
        });
        var empty = array.filter(function (item) {
            return !item[key]
        });
        return present.concat(empty);
    };
});

angular.bootstrap(document, ["app"]);

see working example here

Durgpal Singh
  • 11,481
  • 4
  • 37
  • 49