Reputation: 2181
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
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
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
Reputation: 2039
What's wrong in just doing ?
<ul>
<li ng-repeat="row in rows">{{row | myFilter:anotherArgument}}</li>
</ul>
Upvotes: 0