David Smith David Smith - 1 month ago 8
AngularJS Question

AngularJS ng-if not deleting element after $compile

I have a directive that does the following:

  1. Adds another directive attribute to the element.

  2. Removes its own attribute.

  3. Calls
    on the element to make AngularJS re-compile the element so the new directive is attached.

This works fine, except when I also add an ng-if to the element. See this minimal example and follow the steps below to demonstrate.


  1. Press
    any number of times to add to "count".

  2. Press
    to reset "count".

  3. Press
    any number of times again.

I'd expect the "my-test shown"
tag to be deleted from the DOM once its
condition is no longer true after step #2. Instead, it stays around, and you'll see an extra copy of the message after step #3.

I assume calling
in the my-test directive link function is having some unintended consequence, but I don't understand what's going on here. Any ideas?



As others have answered, the short solution is to use ng-show instead of ng-if or to not use $compile like that. With that aside, you might have your good reasons why you would want to use ng-if and $compile like this.

This question interested me on the note of using $compile with an isolate scope from ng-if. I did a bit of experimenting with this fork and will try to explain what I found.

We already know ng-if creates an isolate scope, but then passing that element with ng-if on it through $compile creates another isolate scope (and would make the newly compiled ng-if be looking at variables on the first-round isolate scope - the directive's $scope value).

To re-iterate that, we're having some scopes looking like (value in [] is scope.$id):

  1. main/outer controller has scope[2]

  2. ng-if my-test element has ng-if looking at scope[2].count and creates scope[3]

  3. my-test linker therefore has $scope.$id == 3;

  4. my-test does $compile - recompiled ng-if element: creates new isolate scope[4] and is looking at scope[3].count

  5. when scope[2].count hits 0 - scope[3] gets $destroyed (because scope[3] was created by that first ng-if which is still lingering around somewhere) ... BUT! the element is A. still there and B. its count isn't updating - WHY?

Well because the element that's still there is the one that was $compiled and has A. an ng-if looking at scope[3].count (which is now $destroyed) and B. its own new isolate scope[4] (created by re-compiling ng-if element with parent scope[3])

So ya. That is all very confusing and you might just be asking... well how do I fix this??


The simplest solution: $element.removeAttr('ng-if'); before you do $compile($element)($scope);

If you've been following along, this works because the original ng-if is still looking at scope[2].count, and the element that is present is no longer getting a second isolate scope.