David Smith
David Smith

Reputation: 43

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 $compile() 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.

https://embed.plnkr.co/ymk0RwGopGF1KvesWmvA/

  1. Press + any number of times to add to "count".
  2. Press 0 to reset "count".
  3. Press + any number of times again.

I'd expect the "my-test shown" <p> tag to be deleted from the DOM once its ng-if 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 $compile($element)($scope); in the my-test directive link function is having some unintended consequence, but I don't understand what's going on here. Any ideas?

Thanks, David

Upvotes: 4

Views: 806

Answers (3)

plong0
plong0

Reputation: 2188

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??

TL;DR;

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.

Upvotes: 1

Ali BARIN
Ali BARIN

Reputation: 1920

As far as I can understand, when you change the value of count with 0, your directive is destroyed before changing the value of count. So that unremoved directive's count value is still 1.

If you use ngShow instead of ngIf, you can solve this. Because ngShow attribute doesn't trigger $destroy event and doesn't remove element from your view. So that directive can catch new value of that count. Or you can use prelink instead of link for catching updating of value of count.

Upvotes: 3

Gustavo Gabriel
Gustavo Gabriel

Reputation: 1296

I dont know how to explain properly but ng-if adds a new scope to the element, his own scope, check this so question to see more details: what is the difference between ng-if and ng-show/ng-hide. I tried with ng-show and it worked the way you want:

ng-show="count > 0"

Hope it helps =)

Upvotes: 1

Related Questions