YoroDiallo
YoroDiallo

Reputation: 345

Hide parent element in DOM when children are empty

<div>
  <h1>Birds</h1>
  <ul>
    <li ng-if="bird.type === 'bird'"
        ng-repeat="bird in creatures">{{bird.name}}</li>
  </ul>
</div>

I have a response from the server and I want to put it into the list. But when the list is empty I need to hide this div container. For example if bird.type === 'bird' is not in the array - I want to hide div. But I want to use bird after ng-repeat so I cant make ng-if="bird.type === 'bird'" on div. Can I check if li is empty, then hide the div?

plunkr example

AngularJS ng-repeat handle empty list case - It's not the same. I don't want to hide li if it empty, I want to hide parent where I have h1 - when li is empty.

Upvotes: 3

Views: 1570

Answers (5)

Iceman
Iceman

Reputation: 6145

Using ng-show="cats.length" to make div's disappear if length is zero.

Filter inline based on object property like cat in creatures | filter:{type: 'cat'} as cats as discussed in this SO post.

WORKING EXAMPLE:

var app = angular.module('App', []);
app.filter(birdsFilter);
function birdsFilter(creature) {
    return creature.type == 'bird';
}
app.controller('Ctrl', function($scope) {
  $scope.creatures = [
      {
        name : 'Cho-cho',
        type : 'bird'
      },
      {
        name : 'Floo-floo',
        type : 'dog'
      },
            {
        name : 'Pou-pou',
        type : 'bird'
      },
      {
        name : 'Oop-flup',
        type : 'bird'
      },
            {
        name : 'Chio-mio',
        type : 'cat'
      },
      {
        name : 'Floo-floo',
        type : 'dog'
      },
            {
        name : 'Loo-Li',
        type : 'dog'
      },
      {
        name : 'Pops-Mops',
        type : 'bird'
      },
            {
        name : 'Boo-Moo',
        type : 'dog'
      },
      {
        name : 'Iop-Pio',
        type : 'dog'
      },
            {
        name : 'Floop-cho',
        type : 'bird'
      }
      
    ]
});
<!DOCTYPE html>
<html ng-app="App">

<head>
  <script data-require="[email protected]" data-semver="1.5.7" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
  <link rel="stylesheet" href="style.css" />
  <script src="script.js"></script>
</head>

<body ng-controller="Ctrl">
  <div ng-show="birds.length">
    <h1>Birds</h1>
    <ul>
      <li ng-repeat="bird in creatures | filter:{type: 'bird'} as birds">{{bird.name}} </li>
    </ul>
  </div>
  <div ng-show="dogs.length">
    <h1>Dogs</h1>
    <ul>
      <li ng-repeat="dog in creatures | filter:{type: 'dog'} as dogs">{{dog.name}} </li>
    </ul>
  </div>
  <div ng-show="cats.length">
    <h1>Cats</h1>
    <ul>
      <li ng-repeat="cat in creatures | filter:{type: 'cat'} as cats">{{cat.name}} </li>
    </ul>
  </div>
  <div ng-show="fishes.length">
    <h1>Fish</h1>
    <ul>
      <li ng-repeat="fish in creatures | filter:{type: 'fish'} as fishes">{{fish.name}} </li>
    </ul>
  </div>

</body>

</html>

Upvotes: 1

frosty
frosty

Reputation: 21762

IMO, several of these answers will work. But none of them are ideally optimized. I would recommend filtering your data in your controller/postlink function.

$scope.animals = {
    dogs: $scope.creates.filter(function(a){return a.type == 'dog'}),
    cats: $scope.creates.filter(function(a){return a.type == 'cat'}),
    birds: $scope.creates.filter(function(a){return a.type == 'bird'}),
    fishes: $scope.creates.filter(function(a){return a.type == 'fish'})
};

This way you would only ever process the array of creatures one time, in one spot. The digest cycle would never have to re-eval the array to see if it needed to update the DOM. Here is what you markup with look like then:

<div ng-if="animals.birds.length">
  <h1>Birds</h1>
  <ul>
    <li ng-repeat="bird in animals.birds">{{bird.name}}</li>
  </ul>
</div>

Upvotes: 2

Rafael Teles
Rafael Teles

Reputation: 2748

In your example I advise you to use a filter instead of using "ng-if", you should create a filter like:

angular.module('moduleName').filter(birdsFilter);
function birdsFilter(creature) {
    return creature.type == 'bird';
}

With the filter you can rewrite your code like this:

<div ng-hide="birds.length">
  <h1>Birds</h1>
  <ul>
    <li ng-repeat="bird in birds = (creatures | filter:birdsFilter)">{{bird.name}}</li>
  </ul>
</div>

Upvotes: 2

frosty
frosty

Reputation: 21762

You could do the following:

<div ng-if="hasBirds(creatures)">
  <h1>Birds</h1>
  <ul>
    <li ng-if="bird.type === 'bird'"
        ng-repeat="bird in creatures">{{bird.name}}</li>
  </ul>
</div>

And then in controller/directive you can add the hasBirds function.

$scope.hasBirds = function(list){
    return list.filter(function(item){return item.type === 'bird'}).length > 0;
}

This hasBirds function would get called often, but would allow you to hide/show the heading of the birds exist.

Upvotes: 4

mcgraphix
mcgraphix

Reputation: 2733

You should filter the list based on the type, store the filtered items in a scope property then use that property to show or hide the div.

<div ng-show="birds.length">
  <h1>Birds</h1>
  <ul>
    <li ng-repeat="bird in creatures | filter:birdType as birds">{{bird.name}}    </li>
  </ul>
</div>

Then implement the birdType filter function in your controller:

$scope.birdType = function(creature) {
    return creature.type === 'bird';
};

Upvotes: 1

Related Questions