Angular viewport watch not reducing the number of watches - angularjs

I am using the Angular viewport watch directive as per the instructions on https://github.com/shahata/angular-viewport-watch. The application I have is based on Angular 1.4.9.
I have a large number of items (or cards), and each card has many watches. The visible number of cards at any time is very few. However, with or without the Angular viewport watch directive, the number of watches remains the same. I feel certain I have used the directive correctly and have added the JS files and the module dependency. I also cleared my cache and such. I see no console errors either. This is on Chrome.
I am checking someone has used the Angular viewport watch directive recently, and if it worked for them. Any tips or tricks are appreciated.

It doesn't reduce the number of watchers, but it disables the ones that are outside the viewport so the total time spent by processing them is reduced.
Legitimate ways to lower the number of watchers would include the use of bind-once, reducing the number of expressions, or using virtual rendering.
One example of virtual lists is Angular Material's virtual-repeat:
https://material.angularjs.org/latest/demo/virtualRepeat

Related

Prevent less time in angular js web application

I have collected all records from angular js batarang but, I don't know how to prevent less time in loading sites and how to know about which range of watcher is correct. Please help me to find this.
Thank you.
Generally 2000 watchers are considered OK per controller. If your watchers count is going above that try to minimize the watchers.
Keeping watchers under limit reduces the time consumed by $digest and $apply cycles and your application remains smooth and responsive.
Watchers are used to track variables defined with $scope. So, use $scope for variables that are going to be used in View or variables were two way binding is required. If you are storing temporary result or variables (that are not going to be changed) use simple JavaScript variables with var.
If your controller is large try to divide the same functionality into 2-3 separate controllers to reduce the watchers at a particular screen.
Use ng-repeat with limit for large arrays.

Too many listeners and slow $compile, need performance tips or something between two-way binding and one-time binding

Sorry, I don't have a live example, I'm looking for performance advice.
I've finally hit my first performance problem with angular, I have a pretty complex UI and in it, I have a directive with about 3 nested repeats that use directives, in every level the directive uses scope: true and using bindToController syntax.
The data source is not that big, but every repeat ends up being between 30-100 watchers (I'm using this snippet to count the watchers)
I'm calling $compile on the top directive and it takes way over a second (!) to show the HTML (it ends up having a few hundred listeners), ng-if and ng-class and other custom directives, it quickly adds up.
I've added one-time bindings wherever I could, but I'm still at way over a 1000 in total and I guess that can slow things down?
I've ran CPU profilers in different browsers, drilled as deep as possible and I never saw my code taking up and significant time, all I see it jquery/angular taking a long long time, but none of the functions inside show any significant self time.
If I open that 800ms elemData.handle, all I see is angular's $scope.eval, compileDirectives and a bunch of other angular stuff in a deep tree.
Could using scope: true be the culprit? Would complex directives perform better if they use an isolate scopes?
Maybe there are advanced methods I don't know to use one-time bindings? Or something between two-way and one-way?
EDIT: for posterity, this is what happened: I managed to replace my isolate scope directives on the inner-most ng-repeat level with simple ng-includes, the functionality is the same, but execution time is 1/1000. I don't think I'll ever really know why the difference was so huge, I never had many items in the ng-repeat, but the code now runs in 1ms :)
Use $destroy events to clean up event watchers
Use one-way-binding everywhere what you can't change manually (ng-repeat, ng-options)
In templates try to avoid functions that will return value. Simple assignment not needed to be compiled but function will run each $digest (ng-if="ctrl.isDataLiading()" slower then ng-if="ctrl.isDataLoaded")
In list items with a lot of ng-repeat avoid filters that will recount and run collection watchers on arrays that is taking great portion of performance - instead use ng-if to remove filtered items
When I was implementing tree structure that can be like expanded to see child nodes (list with + option) I struggled with such problem. Above approach made functionality better but speed did not rise a lot.
In common words all my answer means: angular populates too many watchers, get rid of them

Angular view performance related to ng-app/ng-controller placement in DOM

I was wondering whether I could gain any significant performance difference in my Angular app (specifically view processing) if I change ng-app and ng-controller attribute placement from i.e. body to some inner-page block element with much smaller DOM subtree?
Suppose I have a very long page with huge amount of content, but only part of it is Angular powered. Everything else is either server generated which means it's somewhat static from the client-side PoV.
Would it be better to put ng-app/ng-controller on only that subnode where Angular actually executes or would it be the same if I put them on body element of this very long page?
Does Angular process the view only of the sub-DOM where ng-app/ng-controller are defined or does it process the whole DOM anyway?
Is there any proof about this or even Angular documentation?
Theoretically/Potentially? Yes, as New Dev mentions, the bootstrap function will have a larger DOM to run compile on, which would take longer than compiling a smaller tree.
Practically? Probably not. But your best bet is to benchmark your own page.
As an experiment, you can try the following. I generated a simple random DOM and injected it into a JSFiddle with console.time starting at script load, and ending when the controller is ready. There's a small sub-tree beside (as a sibling) a much larger, 5000-node tree.
Here's the fiddle wrapping the entire body: http://jsfiddle.net/gruagq8d/
And here's the fiddle where only the small subset is used: http://jsfiddle.net/h0hod2j8/
For me, running either of those fiddles repeatedly converges on about 260ms.
I've also tried running similar code on real webpage sources, such as StackOverflow itself, and found the same results (however I did not publish any of these because publishing other people's real pages to JSFiddle does not feel proper without permission) - you can try this for yourself pretty trivially.
Admittedly, this doesn't follow great benchmarking methodology, but if wrapping lots of additional DOM nodes caused significant different performance I'd still expect at least some difference in these.
I don't think that the additional compile time is an issue; for small DOMs it's obviously fast, and for large DOM is very relevant compared to the time it takes your browser to build the DOM in the first place.
All said, as mentioned before, your best option is to try to run similar benchmarks with your own page.
EDIT: Modifying the same benchmark to test digest cycles shows that there's no significant difference in those as well:
Wrapping minimal: http://jsfiddle.net/fsyfn1ee/
Wrapping whole DOM: http://jsfiddle.net/04tdwxhp/
ng-app really just specifies the root element on which Angular calls angular.bootstrap. bootstrap starts a "compilation" process, which is to say that it goes over each DOM element in the subtree of the root and collects directives from it and links them.
Right there, you can see the benefit of restricting the app to a smaller subtree of the DOM:
Compilation process is slightly faster
Less directives are compiled (e.g. <input> elements that should not be part of the app are not compiled/linked).
The thing to remember is that it is the $digest cycle that is the significant source of performance problems/opportunities for optimizations - that is, the number of $watchers and how fast the "$watchers" are.
Official docs say that only portion that is wrapped into ng-app directive is compiled.
If the ng-app directive is found then Angular will:
load the module associated with the directive.
create the application
injector compile the DOM treating the ng-app directive as the root of
the compilation. This allows you to tell it to treat only a portion of
the DOM as an Angular application.
This is pretty much expected as Angular allows to have several Angular modules controlling independent parts of one page (requires manual bootstrapping as pointed by Josiah Keller in comments). And their scopes will not interfere.
However adding extra static html (not angular bound) affect performance only at bootstrap. Yes angular has to compile all that elements at bootstrap to learn whether it should deal with them later. But run-time performance is mostly affected by $watchs. Implicit form of creating them is to do bindings. So the more bindings you have overall, the longer each $digest cycle takes. And gives a general feeling of slow app. I've met a sensible threshold of 2k watches for modern browser/CPUs

How can I pause an AngularJS directive unit tests in such a way that I can observe and interact with the UI?

Motivation
When testing directives; sometimes assertions fail in non-obvious ways, stack traces are not helpful and inspecting local variables while paused on a breakpoint with Chrome Developer Tools hides the forest behind the tree.
This is when I wish I was able to pause a unit test (possibly with iit or ddescribe) and poke around the UI of the directive to narrow down the faulty behavior.
So far
I have been able to achieve this with KnockoutJS before and found it to be an exceedingly useful technique in complicated scenarios. Now I would like to do the same for AngularJS.
I have found that I can attach the element I $compiled with Angular to the html body, and that makes it visible, however I cannot interact with it, as if all the bindings were turned off.
Upon closer inspection I found that while running a test case (an it block) everything seems to be in order, as soon as the test case finishes all the event listeners vanish from the corresponding DOM elements — like click listener on a <button ng-click="...">.
After some digging I found that deep inside angular-mocks.js:1965 there is an afterEach block that calls .off() to turn off DOM event listeners at two places:
injector.get('$rootElement').off(); and
handle && angular.element(handle.elem).off(); (via angular.mock.clearDataCache();)
When I comment out the two .off() lines, voilà, the appended directives jump to life in the DOM!
Note: While the version of angular-mocks I link to (v1.2.10-build.2148+sha.fced1c0) is not bleeding-edge, newer ones only seem to differ in line numbers being shifted when it comes to this part.
Question
Is there a better (more supported, less ad-hoc, and/or less hackish) way to achieve what I am trying to do? If so, how?
Edit: versions
Karma 0.10.9
Jasmine 1.3.1
Angular Mocks v1.2.10-build.2148+sha.fced1c0
AngularJS 1.2.10-build.2148+sha.fced1c0

how can I exclude an element from an Angular scope?

my premise was wrong. while AngularJS was certainly slowing things down, it was not due to the problem I describe below. however, it was flim's answer to my question - how to exclude an element from an Angular scope - that was able to prove this.
I'm building a site that generates graphs using d3+Raphael from AJAX-fetched data. this results in a LOT of SVG or VML elements in the DOM, depending on what type of chart the user chooses to render (pie has few, line and stacked bar have many, for example).
I'm running into a problem where entering text into text fields controlled by AngularJS brings Firefox to a crawl. I type a few characters, then wait 2-3 seconds for them to suddenly appear, then type a few more, etc. (Chrome seems to handle this a bit better.)
when there is no graph on the page (the user has not provided enough data for one to be generated), editing the contents of these text fields is fine. I assume AngularJS is having trouble when it tries to update the DOM and there's hundreds SVG or VML elements it has to look through.
the graph, however, contains nothing that AngularJS need worry itself with. (there are, however, UI elements both before and after the graph that it DOES need to pay attention to.)
I can think of two solutions:
put the graph's DIV outside the AngularJS controller, and use CSS to position it where it's actually wanted
tell AngularJS - somehow - to nevermind the graph's DIV; to skip it over when keeping the view and model in-sync
the second option seems preferable to me, since it keeps the document layout sane/semantic. is there any way to do this? (or some, even-better solution I have not thought of?)
Have you tried ng-non-bindable? http://docs.angularjs.org/api/ng.directive:ngNonBindable
<ANY ng-non-bindable>
...
</ANY>

Resources