Filter on deep object properties in angularjs
AngularJS provides a neat way of filtering arrays in an ng-repeat
tag by piping elements to a built in filter
filter which will filter based on a predicate. Doing it this way you can filter items based on a function, or an expression (evaluated to a literal), or by an object.
When filtering by an object you can pass in a javascript object that represents key value items that should match whats in the backing data element. For example:
$scope.elements = [{ foo: "bar" }, { foo: "biz" }]
--
\<div ng-repeat="foos in elements | filter: { foo: "bar" }"\>
matches "bar"
\</div\>
Here I filtered all the objects whose foo property matches the value “bar”. But what if I have a non-trivial object? Something with lots of nested objects? I found that passing in the object to the filter was both unweidly, and error prone. I wanted something simple where I could write out how to dereference it, like obj.property.otherItem.target
.
Thankfully this is pretty easy to write:
function initFilters(app){
app.filter('property', property);
}
function property(){
function parseString(input){
return input.split(".");
}
function getValue(element, propertyArray){
var value = element;
\_.forEach(propertyArray, function(property){
value = value[property];
});
return value;
}
return function (array, propertyString, target){
var properties = parseString(propertyString);
return \_.filter(array, function(item){
return getValue(item, properties) == target;
});
}
}
And can be used in your html like this:
\<ul\>
only failed: \<input type="checkbox"
ng-model="onlyFailed"
ng-init="onlyFailed=false"/\>
\<li ng-repeat="entry in data.entries | property:'test.status.pass':!onlyFailed"\>
\<test-entry test="entry.test"\>\</test-entry\>
\</li\>
\</ul\>