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=''">×</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.
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>