AngularJS - How to use ng-if without HTML element
Asked Answered
H

6

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.

Herniorrhaphy answered 1/11, 2013 at 5:38 Comment(3)
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 htmlMilliner
Look at #13126399 and #23013441Vinni
In Angular2 (in case anyone arrives here by accident), use ng-container: <ng-container *ngIf="true">Poundage
T
59

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.

Tiger answered 28/6, 2015 at 14:44 Comment(0)
E
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/

Esquibel answered 24/2, 2015 at 12:44 Comment(0)
P
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.

Pegeen answered 12/6, 2015 at 4:45 Comment(0)
C
-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'}
    }
});
Cathedral answered 16/6, 2014 at 8:30 Comment(2)
:( . Nah.. This solution is mixing view with the controller. I want to keep these separate.Herniorrhaphy
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.Summary
F
-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

Ful answered 12/6, 2015 at 4:13 Comment(1)
Could someone explain why this is down voted? And yes. It should be down voted. But no one has said why.Rundown
K
-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>
Kendrickkendricks answered 1/11, 2013 at 5:48 Comment(5)
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?Herniorrhaphy
@Herniorrhaphy - Then use a parent div on top of the div's with ng-if <div ng-if="checked">Kendrickkendricks
@Herniorrhaphy Or you can create a arbitary tag <mylist> on top of all the divs jsfiddle.net/adiioo7/9mgTS/2Kendrickkendricks
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.Herniorrhaphy
I believe @Herniorrhaphy 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.Lepine

© 2022 - 2024 — McMap. All rights reserved.