angularjs directive with ng-if in the template - javascript

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

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.

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.

Related

Transcluding without running digest on contents (passing raw HTML) in Directive

I am trying to pass raw HTML inside a directive for later transclusion (when I open a modal - I will use the HTML to populate it)
The problem is - that if I do it like this, the contents of dialog-body get run and the ng-repeat is implemented, which means when I pass the template along, it gets run again causing an ahem stack overflow.
Is there a way for me to use transclusion or something else to pass the inner HTML without running any digest on it?
<dialog-body>
<div ng-repeat="item_value in item.values">
{{item_value.string}}
</div>
</dialog-body>
By the way, I figured out how to get this done.
Angular was running ng-repeat on the initial code, then re-running it once I used it later on. To avoid this - I did this to skip the first ng-repeat run:
<dialog-body ng-non-bindable>
<div ng-repeat="item_value in item.values">
{{item_value.string}}
</div>
</dialog-body>
This meant that I was only passing the actual HTML and not running Angular on it. I then used a querySelector to find all the ng-non-bindable attributes and remove them. Seems to work fine.
Hope this helps someone!

Angular directive loading order.

I have a view containing two nested directives. The inner-directive depends upon the outer-directive to be present. When I load the page from scratch the directives are loaded as expected i.e the outer exists before the inner. But when I navigate from the page and then return, the order of directive-loading is reversed i.e the inner-directive is loaded before the outer-directive. Both directives are costume directives. When I change the outer-directive to instead being a ng-controller, the problem is solved. To perform the navigation ui-router is used. Does anybody know how to solve this issue without having to use a ng-controller as the outer-directive? And why ng-controller solves the problem?
<div outer-directive>
<inner-directive></inner-directive>
</div>
Without knowing a ton about your specific directives, you can take a look a this blog post about directive priority.
(Controller gets processed in a different cycle than directives, so it makes sense that putting whatever 'dependent' code inner-directive requires into a controller solves your problem).
Here's the interesting part: Angular will follow, it appears, this "path" for your nested directives:
outer-directive: compile
inner-directive: compile
outer-directive: pre-link
inner-directive: pre-link
inner-directive: post-link
outer-directive: post-link
The linked blog post explains "why," but this processing order explains your discrepancy between initial page load (where compile is run for the directive) and your page-back case (where the directive is already compiled and we just need to link).
Again, without knowing much about your directives, maybe you need to relocate the dependent outer-directive code into pre-link (instead of link) so that it gets processed first?
You do that by breaking up your Link object into:
Link: {
pre: function(){
},
post: function() {
}
}
Still need help, [here's a decent post] (http://www.undefinednull.com/2014/07/07/practical-guide-to-prelink-postlink-and-controller-methods-of-angular-directives/) on the breaking up link into pre/post.

AngularJS : ng-if and ng-repeat not working after custom transclusion

I am attempting to do custom transclusion of a directive in angularJS as I need to transclude two separate elements into two separate locations.
The issue is that whilst some directives carried over on the transcluded content work such as ng-bind others such as ng-if or ng-repeat do not, even after recompiling the transcluded elements with the correct scope.
Example: http://jsbin.com/menakapoma/1/edit?html,js,output
As you can see in the example the ng-bind works but the ng-if does not even though they are both on the same scope and accessing the same value. Neither the true or false state of the ng-if works.
I believe this is because the ng-if directive gets transcluded as a comment, however even if I set the priority of the transcluding directive to 9999 and perform it in the pre-linkage function it still does not work.
Does anyone know how to get these directives working?
The issue is that no matter what by the time the transclude function executes the nested directives have already been compiled and replaced with comments.
I have managed to achieve this by completely foregoing the transclude options, and manually requesting the template via $templateRequest.
I specify a compile function which replaces the element with a comment placeholder to ensure nothing is rendered during the request.
In the linkage function I manually compile the template and then replace directive's element with it.
See the updated example here: http://jsbin.com/rocedarono/3/edit?html,js,console,output
It certainly doesn't feel like the cleanest/optimal solution and I am open to any other solutions that can do it a little nicer. Especially as any DOM events have to be bound to after the $templateRequest promise is resolved and checked for existence before removal on the $destroy event to ensure it resolved beforehand.

Accessing the dom outside ng-app

Say I have an angularJS application, where I want to modify the dom. It makes sense that one should not do it via a service or a controller. Instead use a directive for DOM manipulations. But what should one do if the dom lies out of the scope of the angular app?
For Example:
Say I want to access the BODY tag and disable the scroll bars on it. I can't write a directive because ng-app directive is on a div which is deep inside the dom and thus the BODY tag is not accessible.
How does one tackle this problem? I read this blog, and it seems like it ok to do dom manipulations via a service. So what is really the best practice to access doms? Especially if its out of the scope of an angularjs app?
I'm not sure I understand without seeing your code. But in general, the ng-app should be at the highest tag that encompasses all the functionality you want to manipulate with angular. For most, that is the body tag itself.

Difference between the directives and static DOM elements in AngularJS

I have already read the
Compilation process, and directive matching
on AngularJS Doc.
but I really didn't understand the directives.
Example:
I have static html:
<div class="test test2" cid="549" sid="a5e3c4f8a9">text-text-text</div>
when I do it manually, I know it will only created and called one time at browser's parse time.
but what happens when I create a directive with the same dom element ?
<x my-directive>text-text-text</x>
is this the same effect?
I am asking such a newbie question, because I am using over 200 elements on my html page.
If I change them to single directive: for sure it will be much more easier to manage them.
and its no problem if its only slow at browser's compile time but what happend in run time?
and I am sorry, if the qustion is not pro enought. I am just new to Stackoverflow.
Thank You
Daniel
If I understand you correctly, you want to know how AngularJS creates a directive and how many times your directive methods are called.
When you create a directive (with module.directive('myDirective', ...)), you are really just creating a definition. Every time you use that directive (like <div my-directive>), AngularJS will run through the process described in the guide: that is, it will compile and link each use. It has to be this way because the directive doesn't exist in isolation; it exists not only in the $scope in which it was called, but also it can make use of element attributes and transcluded contents. The definition occurs once, but each instance is compiled and linked.
Once the directive is created, it's technically done; if you don't set up any $watch or $observe or event bindings, your "directive" is now just whatever's in the DOM at the end of your link function - there's no more computation. In other words, what happens after compilation and linking is entirely up to you.
So back to your example: if you use 200 of the same directive on the page, the directive will be defined once, but all 200 will be compiled and linked separately. But I'm not really sure what you're implying by asking. What's the question behind your question?

Categories

Resources