5

I'm building a directive that utilizes an ng-if inside of it's template. What's strange is that the element provided to the link function does not have the ng-if code expanded, it is only a comment line for the ng-if. Playing around, I found that by wrapping my link code in a $timeout seems to get it to work, but I am wondering if that is not the correct way of going about it....and more so, why is this happening.

I've added a plunk to demonstrate: http://plnkr.co/edit/Gl7v8yJLevi664nUKcFY?p=preview

zoidberg
  • 167
  • 1
  • 7

2 Answers2

6

Most directives actually do most of their logic in a $watch(). For example ng-if will setup a watch on it's attribute, and then render/remove the dom when that changes. Watches execute during a digest cycle, so even though directives have been compiled and linked, the watch hasn't run yet to determine whether it should show the if or not.

EDIT:

You should probably think about what you are actually doing to make sure if it's what you want. Keep in mind that the ng-if is dynamic. And so at any time it might get removed or added based on whether the items array is empty or not.

This means that even if you manage to defer your dom manipulation until after the if is rendered, you run the risk of the if going away and coming back (at which point your css code will not be run again).

A much better way to do this would be to setup a watch and add your css in a watch, or better still, use ng-class and add the css in your template.

Daniel Tabuenca
  • 13,147
  • 3
  • 35
  • 38
  • Although, $evalAsync is not working in my example...so not sure what to do – zoidberg Jan 03 '14 at 17:50
  • I was incorrect about the $evalAsync, it schedules it right before the next digest not after (although it's not guaranteed as per the documentation). there is a $$postDigest you can use if you want. But see the edits to my answer to understand why this is not a good idea. – Daniel Tabuenca Jan 03 '14 at 19:03
0

The angular framework needs to have $scope.$apply() called in order to update the bindings and expand the template. $timeout() is an async wrapper which assumes that changes were made outside angular world and it calls $scope.$apply() as a final step. in your case, using $scope.$apply(); directly, immediately before your element call achieves the desired effect.

Claies
  • 22,124
  • 4
  • 53
  • 77
  • Hmm, I knew that timeout did that under the hood, but I'm just confused about why I would need to do something like this. I thought that the postlink function is there after all compilation is done and a node has been populated with the template. Is it just not populated with the result of the template expansion and that happens later? – zoidberg Jan 03 '14 at 04:28
  • well in your case, the compile is run first, then the link runs and populates the DOM with a template that needs to be compiled again. – Claies Jan 03 '14 at 04:32
  • relevant: http://stackoverflow.com/questions/15660940/can-not-find-element-in-postlink-of-directive-creation-in-angularjs – zoidberg Jan 03 '14 at 05:41