I have a case in which I have nested loops in which the child one is constructed by a filter function that takes parent as the argument. I also have another filter that just does a text comparison. Here is the example
<div ng-repeat="group in groups">
{{group.name}}
<div ng-repeat="material in materials | filter:filterByGroup(group) | filter:search ">
{{material.name}}
</div>
</div>
Now, my problem is that when filter:search
is applied and it filters out all the results in specific group, I would like to hide the group (and not leave the empty group.name
hanging without child elements).
I don't have the materials in the group it self, so I don't have that information in the parent ng-repeat scope. The question is if there is a way I can access the nested ng-repeat and see its count from the parent and hide the parent if that count is 0.
UPDATE
Here is a fiddle that better explains the situation: fiddle
The main problem is that I don't want to associate my materials with groups. I could do that if nothing else works, but it sounds like an overload (since I would then need to basically filter the results twice) if I could do it by just checking the nested loop.
Thanks
A much cleaner solution was suggested HERE.
What you need to do is wrap the relevant area with an ng-show / ng-if based on an expression that applies the filter on the data structure and extracts length. Here is how it works in your example:
<div ng-show="(materials | filter:filterByGroup(group)).length">
<div ng-repeat="group in groups">
{{group.name}}
<div ng-repeat="material in materials | filter:filterByGroup(group) | filter:search ">
{{material.name}}
</div>
</div>
</div>
This allows you to hide complex structures once they are empty due to filtering, e.g. a table of results.
I've seen this use-case a few times, here's my solution:
<div ng-repeat="group in groups">
<div ng-repeat="material in materials | filter:filterByGroup(group) | filter:search ">
<span ng-show="$first">
{{group.name}}<br/>
</span>
{{material.name}}
</div>
</div>
You can use $first or $last within the scope of the ng-repeat to show only for the first and last of each group. if there is no $first, it won't show the group name.
I just implemented this on my blog and updated your fiddle here: http://jsfiddle.net/ke793/1/
I'm not sure if this is the most elegant solution, but it seems fairly simple and it works. I'd love to see how others solved this.
Update: just realized you can use ng-if
to prevent the group name from hitting the dom at all outside of the $first
element. Little bit cleaner than ng-hide/ng-show, which sets display: none
to the extra header every time.
If you can ask for the materials by group and you expect to do that for each group anyway, why not do that right away when you initialize the view and build a model with groups that have materials? If you can do that you can use ng-show to only hide groups that have materials.
You seem to need to know that a group has materials or not somehow? Here's a fiddle without knowing much of the background story:
<div ng-controller="MyCtrl">
<div ng-repeat="group in groups">
<div ng-show="groupHasMaterials(group)">{{group.name}}</div>
<div ng-repeat="material in materialsByGroup(group)">
<div>{{material.name}}</div>
</div>
</div>
</div>
<script>
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
var groups = [
{'name':'group one'},
{'name':'group two'}
];
var materials = [
{'name':'material1'},
{'name':'material2'},
{'name':'material3'}
];
$scope.groups = groups;
$scope.materials = materials;
$scope.groupHasMaterials = function(group){
return $scope.materialsByGroup(group).length > 0;
}
$scope.materialsByGroup = function(group){
return group.name === 'group one'
? [materials[0], materials[1]]
: [];
}
}
</script>
fiddle with groups
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With