Angular 1 has a nifty built-in filter called orderBy that allows for sorting or ordering a list of objects based on a property in the object. For most cases, this is sufficient. However, I recently ran into a case where this wasn't sufficient. orderBy is flexible in that the expression predicate it evaluates for the sort can be a string, a function or an array (documentation for orderBy can be found here). In the example below, we are using a string. This is probably what you will use the majority of time. However, there are situations where it isn't powerful enough.

   // inside controller function with $scope passed in as a parameter
	$scope.items = [
    	{name: 'tom'},
        {name: 'dick'},
        {name: 'harry'}
    ];


<div ng-repeat="i in items | orderBy:'name'">
    
</div>

Where will this fail you? Specifically for cases where you have nested ng-repeats and you want to access the outer items. Take the following example using one nested ng-repeat. Suppose our application represents a real-world situation where we have a list of work yards (each in a different location), each of which has pallets with pumps on them. The pumps are pumping something, we don't care what, and each pump needs to be serviced at a regular interval. Ideally, they should be serviced within a week of previous service but if 2 weeks have passed, we could have a serious problem. Our application therefore assigns a status to the pallet based on how long it's been since last service. Status = 1 if within a week, status = 2 if between one week and two weeks, status = 3 if it's been more than two weeks. The yard should receive a status based on the worst pallet status within that yard, i.e. if one of the pallets has a status of 3, the yard will also receive that status and then we can sort that list by yard status to immediately see which yards we need to attend to first. Our data could look like this in the .js file.

	// within controller we have a collection of pallets whose statuses we've determined based on how long it's been since they've been serviced
	$scope.pallets = [
        { id: 1001, yardId: 1, status: 2 },
        { id: 1002, yardId: 1, status: 2 },
        { id: 1003, yardId: 1, status: 1 },
        { id: 1004, yardId: 2, status: 3 },
        { id: 1005, yardId: 2, status: 1 },
        { id: 1006, yardId: 2, status: 3 },
        { id: 1007, yardId: 3, status: 1 },
        { id: 1008, yardId: 3, status: 1 },
        { id: 1009, yardId: 3, status: 1 }
	];

	$scope.yards = [
        { name: 'Jones Yard', id: 1 },
        { name: 'Brooks Yard', id: 2 },
        { name: 'Bobby Yard', id: 3 }
	];

And our html might look like this:

	<section>
      <div ng-repeat="y in yards | sortYards">
          <h2>{{y.name}}</h2> <p>{{y.status}}</p>
          <!-- Task list inside yard list -->
            <div ng-repeat="p in pallets | sortPallets:y">
                <h2>{{p.id}}</h2>  <p>{{p.status}}</p>
            </div>
      </div>
	</section>

Notice how in both ng-repeats, we have a filter function which we will create. In ng-repeat="y in yards | sortYards", our filter function is called sortYards and it will be passed the yards collection automatically during the filter process. In the second ng-repeat, ng-repeat="p in pallets | sortPallets:y", our filter function is called sortPallets and it is being passed one additional parameter (in addition to the pallets collection that is being passed automatically). In this case, we are passing it y or the yard item. We could pass more arguments if we want by simply using a : after the y and adding the next argument, useful if we have more ng-repeat nesting going on. Herein lies the difference with the orderBy filter that comes built into Angular. orderBy doesn't allow you to pass in additional parameters and doesn't give us the flexibility to do additional things inside our filter as you will see in a moment. The basic thing we will accomplish within our sortPallets filter is to sort the pallets based on status (something we could have already done with orderBy) but we will also attach a status to the y yard we are passing in based on the worst (or highest) status of all the pallets in that yard. We will therefore be able to sort the yards based on the status of the worst (or highest) pallet in the yard. To create an angular filter, you do this, angular.module('app').filter('myFilter', ...). Here's the full filter we are creating for sortPallets:

	(function () {
    'use strict';


    angular.module('app').filter('sortPallets', function () {
        return function (input, yard) {
            var sortedPalletsByStatus = input.sort(function (a, b) {
                if (a.status < b.status) {
                    return 1;
                }
                if (a.status > b.status) {
                    return -1;
                }
                // aStatus must be equal to bStatus
                return 0;

            });
            var overallStatus = sortedPalletsByStatus[0].status;
            yard.status = overallStatus;
            return sortedPalletsByStatus;
        };
    });
})();


Note that the pallet collection (array) is automatically passed to the filter and we can do whatever we want with that list within the filter. In this case, we will sort it and return a sorted list. After we sort it using JavaScript's built-in sort method, we will have the pallet with the highest status in the array index 0th position. We therefore grab that status and assign it to the yard as the yard.status and return the sorted array.

We will use our freshly minted yard.status in our sortYards filter which is very similar to our filter above and simply sorts the list of yards based on yard.status.

	(function () {
    'use strict';
    angular.module('app').filter('sortYards', function () {
        return function (input) {
            return input.sort(function (a, b) {
                if (a.status < b.status) {
                    return 1;
                }
                if (a.status > b.status) {
                    return -1;
                }
                // aStatus must be equal to bStatus
                return 0;

            });
        };
    });
})();


Since there is nothing complex going on here and no need to pass in an additional variable, I gather we could have just sorted this using orderBy rather than creating our own filter.

So, that's how to create your own filter for nested ng-repeats.