62

Is there a way to tell AngularJS not to display the top HTML element which has ng-if directive. I want angular to display child content only.

Angular Code:

    <div ng-if="checked" id="div1">
      <div id="div2">ABC</div>
      <div id="div3">KLM</div>
      <div id="div4">PQR</div>
   </div>

Rendered HTML:

   <div id="div1"> 
      <div id="div2">ABC</div>
      <div id="div3">KLM</div>
      <div id="div4">PQR</div>
   </div>

What I want:

   <div id="div2">ABC</div>
   <div id="div3">KLM</div>
   <div id="div4">PQR</div>

Here is a fiddle. http://jsfiddle.net/9mgTS/. I do not want #div1 in HTML. I just want #div2,3,4 if checked is true.

A possible solution can be adding ng-if to all child elements but I do not want to do this.

3
  • 1
    there's no way to tell angular to not output the parent element without creating a directive with custom template. You can use template:function(el,attrs) to run some logic based on attributes and return the appropriate html Commented Nov 1, 2013 at 12:50
  • Look at stackoverflow.com/questions/13125899/… and stackoverflow.com/questions/23012941/… Commented Jun 13, 2014 at 12:03
  • 2
    In Angular2 (in case anyone arrives here by accident), use ng-container: <ng-container *ngIf="true"> Commented Dec 20, 2016 at 23:03

6 Answers 6

60

There's a currently-undocumented pair of directives, ng-if-start and ng-if-end, that you can use for this. They behave analogously to the documented ng-repeat-start and ng-repeat-end directives, and you can see the unit tests for them if you like.

For example, given the following code:

<ul>
  <li ng-if-start="true">a</li>
  <li>b</li>
  <li>c</li>
  <li ng-if-end>d</li>
  <li ng-if-start="false">1</li>
  <li>2</li>
  <li>3</li>
  <li ng-if-end>4</li>
</ul>

the first four lis will be shown and the final four lis will be hidden.

Here's a live example on CodePen: http://codepen.io/anon/pen/PqEJYV

There are also ng-show-start and ng-show-end directives that work exactly the way you would expect them to.

Sign up to request clarification or add additional context in comments.

Comments

7

If you are happy to create a custom directive, you can apply the ng-if to the children programmatically and flatten the DOM in the process:

Directive Code:

.directive('ngBatchIf', function() {
    return {
        scope: {},
        compile: function (elm, attrs) {
            // After flattening, Angular will still have the first element
            // bound to the old scope, so we create a temporary marker 
            // to store it
            elm.prepend('<div style="display: none"></div>');

            // Flatten (unwrap) the parent
            var children = elm.children();
            elm.replaceWith(children);

            // Add the ng-if to the children
            children.attr('ng-if', attrs.ngBatchIf);

            return {
                post: function postLink(scope, elm) {
                    // Now remove the temporary marker
                    elm.remove();
                }
            }
        }
    }
})

Angular Code:

<div id="div1" ng-batch-if="checked">
    <div id="div2">ABC</div>
    <div id="div3">KLM</div>
    <div id="div4">PQR</div>
</div>

Rendered HTML:

<div id="div2" ng-if="checked">ABC</div>
<div id="div3" ng-if="checked">KLM</div>
<div id="div4" ng-if="checked">PQR</div>

Fiddle: http://jsfiddle.net/o166xg0s/4/

Comments

0

Here's an Angular 1.21 solution:

HTML:

<div ng-app="app" ng-controller="ctrl">
    <div ng-iff="checked" id="div1">
        <div id="div2">ABC</div>
        <div id="div3">KLM</div>
        <div id="div4">PQR</div>
    </div>
    <button ng-click="toggleCheck()">Toggle Check</button>
</div>

JAVASCRIPT:

angular.module('app', []).
controller('ctrl', function ($scope) {
    $scope.checked = true;

    $scope.toggleCheck = function () {
        $scope.checked = !$scope.checked;
    };
}).
directive('ngIff', function ($compile) {
    return {
        compile: function compile(tElement, tAttrs, transclude) {
            return {
                pre: function preLink(scope, iElement, iAttrs, controller) {
                    var bindingElem = iElement.attr('ng-iff');
                    var children = iElement.children();

                    angular.forEach(children,function(c){
                        angular.element(c).attr('ng-if',bindingElem);
                    });

                    $compile(children)(scope, function(newElem){
                        iElement.replaceWith(newElem);
                    });
                }
            };
        }
    };
});

JSFIDDLE.

Comments

-1

I dont understand why you dont want to use a parent div, but this would be a good solution if you have +20 divs:

HTML

   <div ng-repeat="div in divs" ng-if="checked" id="{{div.name}}">{{div.content}}</div>

JS

app.controller('myCtrl', function($scope) {
    $scope.checked = true;
    $scope.divs = {
         {'name': 'div2', content:'ABC'},
         {'name': 'div3', content:'KLM'},
         {'name': 'div4', content:'PQR'}
    }
});

2 Comments

:( . Nah.. This solution is mixing view with the controller. I want to keep these separate.
One good reason is if you want to hide or show a group of children, but don't want to add in another tag around those children.
-1

Seems to be a old question but I got similar requirement

<div ng-if="checked" id="div1">
      <div id="div2">ABC</div>
      <div id="div3">KLM</div>
      <div id="div4">PQR</div>
</div>

I managed like this

<mytag ng-if="checked" id="div1">
      <div id="div2">ABC</div>
      <div id="div3">KLM</div>
      <div id="div4">PQR</div>
</mytag>

where mytag is just any name which is not a html element as long as it does not disturb the layout, this works for me

1 Comment

Could someone explain why this is down voted? And yes. It should be down voted. But no one has said why.
-2

Try this out:- http://jsfiddle.net/adiioo7/9mgTS/1/

div1 is the parent container for div2,div3,div4, if you don't want div1 in the output html use this:-

<div ng-app="App">Click me:
    <input type="checkbox" ng-model="checked" ng-init="checked=true" />
    <br/>Show when checked:
    <div class="div2" ng-if="checked">ABC</div>
    <div class="div3" ng-if="checked">KLM</div>
    <div class="div4" ng-if="checked">PQR</div>
</div>

EDIT:-

Using angular js and jQuery:-http://jsfiddle.net/adiioo7/9mgTS/3/

Assign a common class (example myClass) to all elements pertaining to that group.

JS:-

angular.module('App', []);

function myController($scope) {
    $scope.change = function () {
        angular.element(".myClass").toggle();
    };
}

HTML:-

<div ng-app="App" ng-Controller="myController">Click me:
    <input type="checkbox" ng-change="change()" ng-model="checked" ng-init="checked=true" />
    <br/>Show when checked:
    <div id="div2" class="myClass">ABC</div>
    <div id="div3" class="myClass">KLM</div>
    <div id="div4" class="myClass">PQR</div>
</div>

5 Comments

Thanks for your reply but this is not what I am looking for. I do not want to add ng-if to all child elements. Just imagine maintaining 20+ child elements with ng-if. If someone has to change "checked" to "checked2", it has to be done at 20+ places. Do you have any other suggestion?
@waqas - Then use a parent div on top of the div's with ng-if <div ng-if="checked">
@waqas Or you can create a arbitary tag <mylist> on top of all the divs jsfiddle.net/adiioo7/9mgTS/2
IE does not support custom tags. Moreover adding <mylist> does not solves the issue. we still have a parent tag. We want to get rid of parent tag containing ng-if.
I believe @waqas is looking for something more like you see in decent templating engines, such as {{ if (condition) }} <my html...> {{ end if }}. Sad that Angular doesn't seem to be able to do this. Angular seems to force the user to use HTML elements for pure logic scenarios - which is pretty stupid.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.