Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I specify a custom filter function for ng-repeat?

Tags:

angularjs

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.

like image 990
Alex Egli Avatar asked Dec 25 '22 08:12

Alex Egli


1 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.

like image 185
bri Avatar answered Jan 05 '23 15:01

bri