Angularjs - using orderby filter in the controller's scope

Amyth picture Amyth · Dec 17, 2013 · Viewed 20k times · Source

I have an array of objects i.e. filtered and paged and now I would like to order the list items by different object attributes.

I tried the orderBy filter as follows:

<th><a href='' ng-click="reverse = sortParam == 'title' && !reverse; sortParam = 'title'">Title</a></th>

<tr ng-repeat="item in pagedItems|filter:filterParam|orderBy:sortParam:reverse">
    <td>{{ item.title }}</td>
</tr>

This seems to be working fine, clicking on the Title link, orders the row alphabetically or reverse alphabetically depending on the current state.

But the problem here is that only the pagedItems are being ordered, which makes sense as we are applying the orderBy filter to the pagedItems. What I want to achieve is to order the whole set of items (not just currently paged items) to be ordered when the filter is applied.

In order to achieve this I thought i'd use a method in the controller scope. so I changed the above to:

/** In the Template */

<th><a href='' ng-click="sortItems('title')">Title</a></th>

<tr ng-repeat="item in pagedItems|filter:filterParam">
    <td>{{ item.title }}</td>
</tr>


/** In the Controller */

$scope.sortItems = function(value) {
    $scope.filtered = $filter('orderBy')($scope.filtered, value);
};

$scope.$watch('currentPage + numPerPage + filtered', function() {
    $scope.pagedItems = getPagedItems($scope, 'filtered');
});

The sortItems method works and changes the order but the items in the view does not get updated as the $watch code is not fired. I assumed that maybe it's not being changed because the data in the $scope.filtered is not being altered and just the indexes are being changed. So I added and empty element at the end of the array:

$scope.sortItems = function(value) {
    $scope.filtered = $filter('orderBy')($scope.filtered, value);
    $scope.filtered.push({});
};

Now, Everything works as expected but I can't keep an empty object in the array as it affects the items, count and data being displayed. So I thought I'd add and remove an empty item. So changed the above to :

$scope.sortItems = function(value) {
    $scope.filtered = $filter('orderBy')($scope.filtered, value);
    $scope.filtered.push({});
    $scope.filtered.pop();
};

But guess what the $watch code not being fired again.

Question

My question is does $watch look for changes in an array based on it's length ? If yes, what is the best possible way to achieve what I'm trying to. Any help would be appreciated.

Answer

Amyth picture Amyth · Dec 17, 2013

Ok I solved this using $broadcast and $on as follows:

$scope.sortList = function(value) {

    if (value === $scope.currentFilter) {
        value = value.indexOf('-') === 0 ? value.replace("-","") : "-" + value;
    }   

    $scope.currentFilter = value;
    $scope.filtered = $filter('orderBy')($scope.filtered, value);
    $scope.$broadcast('sorted');
}

$scope.$on('sorted', function() {
    $scope.pagedCandidates = getPagedItems($scope, 'filtered');
})