muenchdo
muenchdo

Reputation: 2181

Pass the current element to custom filter in ng-repeat

Is it possible to pass the current element of ng-repeat to a custom filter?

Somehow like this (HTML):

<ul>
    <li ng-repeat="row in rows | myFilter:row:anotherArgument>{{row}}</li>
</ul>

and (Javascript):

angular.module('myApp', [])
    .filter('myFilter', function() {
        return function(items, item, arg) {
            // Do something here
        }
    });

The data I'm dealing with looks like this (simplified):

[
    {
        "id":"52d7d22867960cb905e1c5b3",
        "label":"Model A",
        "children":[
            {
                "id":"52d7d22967960cb905e1c5bf",
                "type":"Page",
                "label":"Sample Page A",
                "class":"page",
                "children":[...],
                "watched":false
            },
            {...},
            {
                "id":"52d7d22967960cb905e1c5bd",
                "label":"Page",
                "class":"type"
            }
        ],
        "class":"model",
        "watched":false
    },
    {...}
]

So basically I have a tree like structure and in the first layer, there may be one or more elements with "class":"type". The nested data is handled by Nick Perkins' angular-bootstrap-nav-tree directive. I want to only display the elements that have "watched":true. The problem is of course, that the elements of "class":"type", don't have the watched attribute.

tl;dr
Filter out "watched":false but keep elements with "class":"type"

Upvotes: 1

Views: 3222

Answers (3)

Dalorzo
Dalorzo

Reputation: 20014

Here is a sample:

Please note that the first argument of the filter is the "Data Source" ( the entire list). It is the purpose of the filter to filter the Data Source.

HTML Sample

<ul ng-controller="myCtrl">
    <li ng-repeat="row in friends |myFilter:2:5 ">{{row.name}}</li>
<ul>

Js App and filter creation

var myApp = angular.module('myApp',[]);

myApp.filter('myFilter', function() {
        return function(items, begin, end) {                                     

            return items.slice( begin, end);
        }
    });

function myCtrl($scope)
{    
    $scope.friends = [
      {name:'John', age:25, gender:'boy'},
      {name:'Jessie', age:30, gender:'girl'},
      {name:'Johanna', age:28, gender:'girl'},
      {name:'Joy', age:15, gender:'girl'},
      {name:'Mary', age:28, gender:'girl'},
      {name:'Peter', age:95, gender:'boy'},
      {name:'Sebastian', age:50, gender:'boy'},
      {name:'Erika', age:27, gender:'girl'},
      {name:'Patrick', age:40, gender:'boy'},
      {name:'Samantha', age:60, gender:'girl'}
    ];
}

Upvotes: 0

Tharabas
Tharabas

Reputation: 3422

Not as you're using it, but there are ways to work with the rows as a whole and separately.

If you need to access the elements of your rows before the ng-repeat content, you should write the filter for the whole array:

<ul>
  <li ng-repeat="row in rows | myFilter:anotherArgument"> ... </li>
</ul>

Within the filter-definition just work with the whole array:

angular.module('myApp', []).filter('myFilter', function() {
  return function(items, anotherArgument) {
    // here you can access each argument as you like
    items.forEach(function(item) { 
      ...
    });
    return items;
  };
});

The main reason for this is that row does not exist until you iterate over the array and the array itself is the result of the whole FilterChain. Think of the array rather like this:

ng-repeat="row in (row | filter1 | filter2 | ...)"

But depending on the purpose of your myFilter you simply might want to apply that filter in the content of the repeat itself.

<ul>
  <li ng-repeat="row in rows">
    {{row | myFilter:anotherArgument}}
  </li>
<ul>

In case you're using the first approach I'd recommend to prepare your array in the controller before you're passing it to the ng-repeat. The more complex the filter chain inside any binding (as {{...}}, ng-bind or ng-repeat) gets, the more has to be calculated within each digest cycle of angular. That may lead to slower applications.

UPDATE According to the updated question:

In your case I guess a custom filter may look like this:

angular.module('myApp', []).filter('mySpecificFilter', function() {
  return function(items) {
    return items.filter(function(element) {
      return element.watched || element['class'] === 'type';
    }
  }
});

Upvotes: 3

Adrian Neatu
Adrian Neatu

Reputation: 2039

What's wrong in just doing ?

<ul>
    <li ng-repeat="row in rows">{{row | myFilter:anotherArgument}}</li>
</ul>

Upvotes: 0

Related Questions