I am trying to use a custom filter function with ng-repeat since my filter value is a complex object. I am using angularJS v1.5 and following the docs here: https://code.angularjs.org/1.5.0/docs/api/ng/filter/filter
The docs say you can specify an expression like this: {{ filter_expression | filter : expression : comparator}} And that the comparator function is called with two arguments, the actual object from the array and the predicate value. My comparator function always has 'undefined' for the actual object. I tried making everything as simple as possible and it still sends in undefined for the actual object.
$scope.athletes = [
{
"name":"first",
"tags":[
"Offense"]
},
{
"name":"two",
"tags":[
"Defense"]
},
{
"name":"three",
"tags":[
"Special Teams"]
},
{
"name":"four",
"tags":[
"Offense"]
}
];
$scope.athletesInFunction = [];
$scope.doesAthleteHaveTag = function(athlete,filterValue){
if (athlete) {
$scope.athletesInFunction.push(athlete);
if (athlete.tags.indexOf(filterValue) > -1) {
return true;
}
}
return false;
};
HTML File:
<div ng-repeat="athlete in athletes | filter:filterValue:doesAthleteHaveTag">
<div>Name: {{athlete.name}}</div>
</div>
Example plunkr here: https://plnkr.co/edit/lk26tFFgux2sLpuke56m?p=preview
What am I doing wrong? How do I get it to send in the actual array objects to filter?
EDIT: I should have been clearer with my question. My question is whether or not the docs are valid, and what the recommended way to use a custom filter function is. I currently just use an expression function instead, since the filter predicate is a scope wide variable and I can access it easily in my expression function. I don't know if that is better, worse, or the same as using a comparator function, or if it is better to just write a custom filter as mentioned in one of the answers.
In order to create a custom AngularJs filter that accepts a paramter, you need to define it as a filter like this. The idea is simple: the filter returns a FILTERED version of your array (this way, you don't blow up the digest cycle with tons of changes to the root array you are observing).
In your case:
1. Create a filter
app.filter('hasTag', function() {
return function(items, tagName) {
var filtered = [];
angular.forEach(items, function(el) {
if(el.tags && el.tags.indexOf(tagName)>-1) {
filtered.push(el);
}
});
return filtered;
}
});
2. Change your filter parameter
<div ng-repeat="athlete in athletes | hasTag:'Defense'">
That should do it.
Option #2 - Change your ng-repeat to use a filtered array.
Because most of my filters are very "controller-specific" -- I tend to use a filtered array as my ng-repeat source.
<div ng-repeat="athlete in AthletesByRole('Defense')">
That approach lets me control the cache myself at the controller level ... and I find it easier to read/maintain (since AthletesByRole() is a function inside the controller instead of a filter function in some random "other" js file that I have to track down 6 months after it was written) ... Just an added thought to consider.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With