4

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.

1 Answer 1

10

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.

Sign up to request clarification or add additional context in comments.

9 Comments

So the docs are wrong? This format: {{ filter_expression | filter : expression : comparator}}, does not actually exist as a valid way to filter an array?
The docs are... a disappointment. I think, when is says 'filter' it mean 'a function defined as a Filter within your angular app.'
Can anyone help me with this one? stackoverflow.com/questions/42213713/…
@Mawg -- you can minimize overhead in the controller (eg - cache your filtered items), so that's a good thing. In the end, though, both of those implementations are just code you control ... so I think it's less about speed and more about code readability.
Also, remember that ng-repeat can be really slow - so you want to limit the dom elements rendered by yours!
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.