While profiling Angularjs app I can see that on a slow page digest runs 5 times and many expressions evaluated too many times and many expensive controller functions are called multiple times as well.
Is there a way to prevent watched expression from being evaluated more than 1 time within a single digest?
Few examples to better illustrate the question:
I have an icon picker with 300+ icons, each icon has {background: colorPicker.color}
I have a very expensive function to check if page is modified and needs saving. It does a deep equals on initial version and current version of a very large object.
So I know that when digest is running I do not need to re-evaluate these.
I would like to have a way to cache calculated values within a single digest cycle.
One time binding is not an option, these watched expressions do change, just not within single digest.
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.
I'm working on a single-page app where some parts are really slow. They're slow because I'm displaying 400 complex things in a repeater for the user to scroll through. Each thing is generated by a fairly complex directive that does a ton of data binding and expression evaluation, adds one or two click handlers, and displays a couple of images. In some cases, I also need a grayscale CSS filter on those images, but that really seems way too slow.
I have of course already turned most of my data binding into one-time data binding, but simply generating the 400 things for the first time is still slow. It's initially hidden through ng-if, which speeds it up when I'm not showing it, but once I do need to show it, everything waits 10 seconds for that to happen. I would like to load it in advance, but using ng-show instead of ng-if means the loading of the entire app has to wait for this.
What I would like, is to load the rest of the app, and then, while we wait for user input, start creating these 400 things so they're ready once I need to show them. I don't want the user to notice how slow this is.
Problem is, I have no idea how to do this. Any ideas?
Edit: Having thought about this (and discussed this with my wife), I'm seeing two options (that I conceptually understand, at least):
The easy, quick, boring and cowardly solution is to simply not show the 400 things at the same time, but cut them in pieces and show 40 at a time. That should make it a lot quicker, but also less nice, as the user needs to click around to access all the data.
The interesting solution is to write a new ng-repeat that generates the 400 transcluded copies of the template each in their own asynchronous event, so they don't block user interaction. I really like this idea, but there's one big downside: it's an ambitious idea with deep Angular magic, and I don't have much time available.
OK, not seeing your code structure, through Q&A, I'm trying to get clarification. If I understand you correctly, I believe the solution is to process your images asynchronously and remove reliance of creating/processing them on the fly when the view is visible (i.e. via clicking on a button/tab to 'show' the array 'view' and thus triggering the ng-repeat). BTW, this solution assumes the delays are because the images are being processed rather than because they are being shown.
METHOD 1 (less preferred)
To do this, it's best to create an 'ImageDataService' service, where it get's kicked off at application start, and proceeds with building this array and taking whatever time it needs asynchronously without caring what view is showing or not. This service will be injected into the proper view or directive controller--perhaps where the current ng-repeat scope is.
Meanwhile, you also need to change the directives inside your ng-repeat to get the pre-built data from the array provided by ImageDataService rather than actually doing the calculation at view time. If the data for that directive is not ready, the directive will show something like 'loading...' otherwise, the directive will simply show the prebuilt images. This way, it doesn't matter when ng-repeat is triggered (i.e. its view is showing), because it will just show what are processed and ready at that point and the display the rest as 'loading...'.
METHOD 2 (I prefer this)
Alternatively, and maybe better, you can forego creating a pre-processed array in a service as in METHOD 1 and instead modify your directives to process their data asynchronously by invoking a service method that returns an asynchronous promise like this:
(code inside a controller)
var promise = ImageDataService.processImage(directiveData);
promise.then(function() {...set the directive image attributes..})
Needless to say, the ImageDataService.processImage() method needs to return a promise when it is done processing the data. The directive, as usual, will show 'loading...' until then. Also, although ImageDataService no longer needs to pre-populate its array mentioned in METHOD 1, it might be a good idea to save the processed images to a similar array anyway to serve as cache and not reprocess them needlessly. NOTE, in this case you don't need to have processImage() inside a service--but it is really good 'separation of concerns' practice to reserve the work of asynchronous data processing (ajax, etc) within a service which can be injected app-wide rather than within a controller.
Either of these 2 general tacks should work.
I want to know if and how it is possible to suspend digest loops until my array of objects have finished the initial rendering.
I have custom templates defined by the objects they receive, the array is dynamic therefore I'm using ngRepeat. The issue I'm having is that every digest loop takes around 15ms in IE11, but I can have 100-200 templates being rendered, which can add up to ~2000ms.
Is there a way to suspend actual digest loop in ngRepeat from outside, or do I have to decorate the directive itself to achieve that?
Note: Generally other browser are really good with performance and we don't have any issues, but IE11 is a special case and we need every single performance gain we can.
I have an AngularJS app with a ng-repeat. This ng-repeat displays only about 60 watchers (the list of ng-repeat contains quite big JSON objects though). In the same page I have an input. When I try to write inside this input, it is very slow (displays one letter every second).
I tried to optimize as much as possible but the inputs in my whole app are slow. If I disable the ng-repeat, the inputs goes back to normal speed.
Can someone explain to me why inputs are slow? And if I can solve this problem?