Is there any case when I don't need an ngCloak directive?
It seems that every tag that somehow uses Angular library needs to define this directive either via ng-cloak attribute or class="ng-cloak".
Is there any downside of using this directive for every possible element?
ng-cloak will cause your element not to render straight away. Often elements will look fine without ng-cloak, in which case it would be strange to opt for a blank space.
This will often be the case if you are not using the {{x}} syntax. Perhaps you could use ng-bind instead.
Lots of elements will not be in the DOM at page load. For example, the template of a directive will obviously not be available until after the digest cycle has already run. It would be strange to try to cloak something which was not visible in the first place.
Using ng-cloak more than you need to will just hurt performance.
Related
I recently started studying about digest and performance improvements in AngulaJs and found on my website that I'm using tons of ng-if.
Sometimes in ng-if there is a variable that may change, but often is fixed at the startup of the controller and then never changes.
What should I do so to improve performance avoiding digest to evaluate every loop those unchangeable ng-if? Should I change directive? With what?
E.g
In my header template I have a div that can be seen only by particular type of user. It's just a div, so I don't want to call some different template.
I put <div ng-if="userIsSuperior()"> ... </div>
When first evaluated, the return vale of userIsSuperior() never changes (during this session of course), but I know that AngularJs Digest evaluates it every loop.
How can I avoid this? Or am I missing something?
I think what you are looking for is one-time binding.
If you use:
<div ng-if="::userIsSuperior()"> ... </div>
Then the value of userIsSuperior() will only be calculated once and will stick to that value.
See demo.
First answer solutions needs AngularJS > 2.
I found a valid solutions in using OnceJS, library for one-time-binding.
Here
I have multiple custom directives in my ngApp. The demo code is:
<top-nav></top-nav>
<left-sidebar></left-sidebar>
<div class="content">
....
</div>
The height of the 'left-sidebar' needs to be adjusted according to the height of the 'top-nav'.
Similarly there are other interdependent UI tasks. I want to run the post-load code (say app.initializeUI();) only after ALL the directives have been loaded and rendered.
Now, How do I achieve that?
EDIT :
Currently I am using the following code :
App.run(function($timeout){
$timeout(function(){ app.init() },0);
});
This runs fine, however, I am not sure this is the perfect way of doing this.
EDIT 2:
For people who want to avoid setting styles in js - use CSS Flexbox. I find it much better than calculating heights post page load. I got a good understanding of flexbox here
I would create an attribute directive with isolated scope, set terminal=true and add it to your body tag. Then in your link function, setup a $watch - isInitialized which is initially false. In your $watch handler, call your app.init(), and then de-register the $watch, so that it is always initialized once.
Setting up a terminal directive has consequences - no other directive can run after the terminal directive on the same element. Make sure this is what you want. An alternative is to give your directive the lowest possible value so that it is compiled first, and linked last.
The important pieces to this solution are:
By adding your Directive to the body tag, you ensure that it is linked last.
By adding a $watch, you ensure that all other directives have gone through a digest cycle, so by the time your $watch handler is called, all other directives should have already rendered.
Note of caution: The digest cycle may run several times before scope changes stabalise. The above solution only runs after the first cycle, which may not be what you want if you really want the final rendering.
In our app, we have several layers of nested directives. In an attempt to speed up the digest cycle, we removed some top level directives that have data-bindings to static data. Instead, we just generated the html from the static data, and compiled that. However, after doing that, we started getting unresponsive script warnings in Firefox. The compile time on this generated html was taking too long.
I'm hoping to gain a better understanding of the compile process in these 2 different scenarios so that we can optimize correctly in the future. Here is a very simplified fiddle that shows the problem. Here is the important part of it:
var repeaterHtml = '<div repeater="allData"></div>';
var staticHtml = '';
angular.forEach($scope.allData, function(currData, currIndex) {
staticHtml += '<div dir1="allData[' + currIndex + ']"></div>';
});
$element.append(staticHtml);
When using the staticHtml it takes significantly longer to compile than when just using the repeater directive. Why is this? Is it purely because there is more html to go through? As angular compiles the repeater directive does it not have to do the same compiling as I was doing manually? When the final DOM is exactly the same, what is it about the repeater directive that makes it compile so much faster?
I'd appreciate any insight into this. Thanks.
EDIT - your repeater directive does nothing special - it is just a wrapper for ng-repeat. To understand why repeater is faster than manipulating the DOM from the controller, you need to understand how ng-repeat works, what are compilation and linking phases. AFAIR Miško Hevery - has a talk about this here http://www.youtube.com/watch?v=WqmeI5fZcho
There are few things you are doing in your example that cause it to be slower than angular ng-repeat
Manipulating DOM in controller - don't do that! This practice is very bad! The reason ng-repeat is faster in this case is because it compiles the DOM once and only produces copies of the repeated element for each element in the array. In your case, you are creating element from string after the DOM has been rendered, then you modify the DOM structure by appending the element to the document, and after that, you are compiling the element you have just created against scope, which again modifies the document's DOM! You only need to compile the element once, and the append it to the parent (but do it in a directive - not in controller).
If you want to have a faster implementation of ng-repeat (but with less features - like auto updating) then write a directive that would do almost the same thing but using single template element and modify the document's DOM only once.
If you want to have better understanding how this works, I suggest reading ngRepeatDirective https://github.com/angular/angular.js/blob/master/src/ng/directive/ngRepeat.js
I'm using the router (was using the built in one, now using ui-route but solutions for either are fine) in Angular.JS to switch between control/template pairs. When switching back and forth between a couple of these pages it takes up to a second to setup the DOM each time which looks terrible. Is there anyway of having angular keep around the DOM tree instead of recreating it each time. I guess I'd like to just hide/sow the bits for each page rather than remove/re-create them each time.
Any suggestions welcome!
You have to write your own ng-view directive to create such functionality.
The basic idea behind it is:
Before the route changes, instead of destroying the current view element, and scope, you just put it in an invisible cache DIV, and deregister scope listeners. Give the element a data attribute with the $$route.templateUrl to be able to get it back.
Then you fetch the next view from the server.
Before the route changes you check for cache item existance, and if it is in your cache div get the element from the cache, re-register listeners and put the current view the cache.
The tricky part is not to mess the $scopes up. So you might need a constructor and destructor in your $scope for the event, and maybe for the $watchers as well. I'm not sure.
But to be honest, if you using the template cache and still takes 1 second or so to render, then you might have some inefficient $watch expression, or a huge ng-repeat. You should consider some refact.
I've been researching this myself. I am working on rather old hardware in an unaccelerated browser. Initial render is a problem. My early work-around was to cache pre-compiled templates, as you are attempting. I found that this provided only a minimal speed improvement.
The real bottleneck was coming from my ng-repeat directives, the number of reflows that resulted and the number of DOM nodes/watchers I was building in each iteration.
Some sources have suggested creating a custom directive which manually assembles the dom and appends it in one go. The result is very minimal reflow and no watchers. The downside is very large. No more angular fun and a lot of unnecessary work. More importantly, none of this provided a large enough speed improvement to justify the work.
I ultimately found that the best speed improvement came from forcing hardware acceleration for each ng-repeat iteration. As suggested above, the ng-animate directive in newer versions angular make this relatively trivial.
You will see immediate page render, with minor reflow hiccups. ng-cloak does not help here. Due to the animation request, the page is not suposed to be cloaked while the repeat renders. These can, however, be rendered reasonably well with a bit of clever fun. I'm actually hiding the ng-repeat until the $location changes, showing a progress indicator in the meantime, and then toggling my ng-show. This works really nicely.
Having said all of that, precompiling your templates should be done as follows.
1) When your app starts, create a new cache for yourself. See http://docs-angularjs-org-dev.appspot.com/api/ng.$cacheFactory
2) Populate this cache with compiled templates. Inject $compile and call it on each template. Compile returns a function which you will later call against your scope. Key this function in your cache as you see fit.
3) Create a custom directive which accepts a cache key as an attribute. Inside this directive, query your compile cache for the correct compile function. Call the function against your current scope, and append the resulting DOM to the element passed into the directive.
4) Sorta win :).
If you move up to Angular 1.1.5, you can use the ng-animate attribute on you ng-view tag.
I'm not 100% sure, but I think it does some DOM caching to make the transitions work better. You could try adding adding ng-animate attribute to your tag. That might take care of it for you.
I know that even if ngShow evaluates to false, it still performs dirty checking inside the directive.
So does ngShow (like ng-show=false):
compose the DOM inside ngShow (since it has to do $compile) and then hides/removes the contents, or
only performs dirty checking without the cost of creating DOM elements?
ng-show will only set the elements to display: none. So they will still be in the DOM and still be dirty checked by angular.
Try using something like ng-switch for actually removing elements from the DOM. http://docs.angularjs.org/api/ng.directive:ngSwitch
Angular.js doesn't create any DOM nodes during the compilation. It lets the browser build the DOM then simply goes through it and collect the directives.
So if the nodes are in your templates, they will indeed be created, but by the browser, not Angular.