ng-repeat filter with filtered properties

I have a list of data that I would like to search through using Angular's typical filtering.

I have an array of objects (one of a few below):

{
   vendor: 'Joan\'s Produce',
   date: '2014-04-07',
   total: 888.11,
   note: 'insert note',
   description: 'fresh produce',
   terms: 'Net 10',
   deliverer: 'Joe Truck',
   paid: true
}

In the <thead> I have input fields to search the date and total fields.

<th style="vertical-align:text-top; width:250px;">Date<br />
    <div class="input-group input-group-sm">
        <input type="text" class="form-control" ng-model="dateQuery.date" />
        <span class="input-group-addon"><button type="button" class="close" aria-hidden="true" ng-click="dateQuery=''">&times;</button></span>
    </div>
</th>

The data in the table has a filter of it's own:

<tr ng-repeat="inv in invoices | filter:dateQuery | filter:amountQuery">
    <td>{{inv.date | date:'MM/dd/yyyy'}}</td>...
    <td>{{inv.total | currency}}</td>
</tr>

The filters within the ng-repeat format the date and change the total to a currency. When I now search those filters, it seems to only search the raw data from which the ng-repeat is pulling from.

Is there a way to build the input filters in a way that they will search the filtered result? For instance I have to search the dates with 2014-04-07, and can't add a $ in the total filter.

I have build a demo here

The filters on ng-repeat will only filter the raw data, this is how ng-repeat works. To filter on the output from the filters in your table data cells, I can see two options:

  • Attach prefiltered values for date and currency to the objects you are filtering. In order to keep things somewhat DRY, you can use the filters themselves for this purpose, in a controller or elsewhere fitting:

    object.currencyFlat = $filter('currency')(object.total);
    
  • Build a custom filter dynamically filtering on the value you present visually in the table:

    angular.module('myModule').filter('myCurrencyFilter', function($filter) {
        return function(array, text) {
            // Could be more sophisticated
            return array.filter(function(item) {
                return $filter('currency')(item).indexOf(text) !== -1;
            });
        }
    });
    

Of these two, the first would be much quicker, as it only formats the object's total as a currency once.

I think neither of these approaches are particularly beautiful. I would love to see a better solution.

I ended up leveraging Underscore.js to run a foreach loop on the scope object

_.each($scope.invoices, function (invoice) {
    invoice.filteredDate = $filter('date')(invoice.date, 'MM/dd/yyyy');
    invoice.filteredTotal = $filter('currency')(invoice.total, '$');
});

Then referenced the new fields in my HTML

<tr ng-repeat="inv in invoices | orderBy:predicate:reverse | filter:dateQuery | filter:totalQuery">
    <td>{{inv.filteredDate}}</td>
    <td>{{inv.vendor}}</td>
    <td>{{inv.filteredTotal}}</td>
    <td>{{inv.terms}}</td>
    <td class="center"><input type="checkbox" ng-model="inv.paid" disabled /></td>
    <td class="center">
        <a ng-click="invoice(inv, $index)"><i class="fa fa-edit"></i></a>
    </td>
</tr>