Limiting the number of watches created while using ng-repeat - angularjs

I am creating a standard table using angular's ng-repeat directive. My table will be carrying 10000 cells. As far as I understand, ng-repeat creates watches on every cell and this can significantly erode the performance of my app. Any clues on how I can restrict the number of watches while ensuring that data binding functions the way it shoul.
I have a plnkr here. The template for table is in the file layout.html
http://plnkr.co/edit/Hahh4uyQ130zOS8noC3D

Related

What is the alternate way to ng-repeat in angularjs?

Generally ng-repeat is used to represent the objects and arrays in the view from the controller.It is used for repeating each element in the group of data.Here I want to know the alternate way for representing the group of data in the view without using ng-repeat.
Although the answer is dependent on situation in which you provide solutions anyway,
As per my development experience ng-repeat is good if you data is very less.and if data is heavy the performance of ng-repeat reduces considerable amount.
Inorder to solve above problem we can go for custom directives
Here are some of the directives which you can look for getting some idea
AngularJS ng-repeat Alternative Approach
This document will give all step by step approach to create custom directives.
This is about alternative approach to ng-repeat to handle heavy data
binding with better page performance. This article will provide
insights of how to replace particular ng-repeat with particular data.
AngularJS directive for much more quicker lists rendering
A custom directive you can add and you can customize based on your requirements with following features
Shallow list watch (ngRepeat uses deep watch)
Animations support
Special service to cause list render outside of digest cycle
Smooth scrolling even on heavy compited lists (check example)
About 200% performance boost
Still hesitating? Try to scroll page with ng-repeat list and a page with quick-ng-repeat
Apart from this i can go for some solutions such as pagination lazy loading etc to improve performance
ng-repeat is good to use when you don't have a large amount of data. There is an advantage using ng-repeat, whenever you use track by $index, it will do some hashing, and when it encounters a similar kind of element as you update the model properties, it won't recalculate the layout for that particular element.Behind the scenes, ngRepeat adds a $$hashKey property to each task to keep track of it.
But ng-repeat is not the right thing to use when you have large datasets as it involves heavy DOM manipulations. And you should consider using ng-repeat with pagination.
You can consider using transclusion inside a custom directive, to achieve the behavior you are looking for without using ng-repeat.

ng-repeat v/s md-virtual-repeat

Difference between angular ng-repeat and angular material md-virtual-repeat?
When should i use one or another?
ng-repeat renders all elements in list, its less performant on large lists.
md-virtual-repeat renders list what is visible on viewport, it doesn't render all elements of list, when user scrolls in case of large lists it then seemlesly renders other elements, this way its performant and should be used when working with large lists.
Angular documentation tells it pretty clearly:
Virtual repeat is a limited substitute for ng-repeat that renders only enough dom nodes to fill the container and recycling them as the user scrolls. Arrays, but not objects are supported for iteration. Track by, as alias, and (key, value) syntax are not supported.
Source
md-virtual-repeat is similar to ng-repeat but it is very useful when you want to load large amount of data.
Consider you have to load a 100,000 records. In this case if it is ng-repeat then it will load all the data initially. So the user may get frustrated while it is loading. If the user wants the first 50 rows of data only, ng-repeat forces him to wait until all 100,000 records load!
To avoid this in material we have md-virtual-repeat. It loads the next set of data when there is a demand for it (the user scrolls for more data)
Ultimately, the loading time is optimized if you use md-virtual-repeat.
The ngRepeat directive instantiates a template once per item from a collection. Each template instance gets its own scope, where the given loop variable is set to the current collection item, and $index is set to the item index or key.
Source
ng-repeat loads the entire data set before rendering to the UI. It is extremely useful when dealing with a smaller list. To ensure that it is most effective it is advisable to use track by and or limit to in the ng-repeat expression. A great md-data-table example that uses ng-repeat is Daniel Nagy's table
With a large list of records ng-repeat becomes very much slower. If it is slow the recommended usage is to switch to md-virtual-repeat
md-virtual-repeat specifies an element to repeat using virtual scrolling.
Virtual repeat is a limited substitute for ng-repeat that renders only enough DOM nodes to fill the container and recycling them as the user scrolls.
Source
md-virtual-repeat only loads the data on demand - the user scrolls. It loads the data much quicker when having a large result set. md-virtual-repeat becomes cumbersome when inserting it into a table.

Does ng-repeat retain DOM elements or create all new ones when the collection changes?

I've seen a lot of questions about the order in which ng-repeat finishes compared to other directives or things going on in Angular land, but I haven't been able to find an answer to how exactly it accomplishes this.
I have two ideas of how it could work.
First Way:
When ng-repeat's watcher triggers, it removes all elements it created from the DOM then creates all new elements in their place, even if many of those elements are the same (e.g. in the case of 1 item added to the backing array).
Second way: Since ng-repeat already keeps track of which elements go with which items in its backing collection, it simply removes the items that no longer exist in the collection and creates new elements for items that are new to the collection.
Which is it and why?
It's the second way: Angular tries to be smart about creating/removing DOM elements:
The ngRepeat directive provides a way to render a collection of items given a template. To do this, AngularJS compiles the given template and then clones it for each unique item in the collection. As the collection is mutated by the Controller, AngularJS adds, removes, and updates the relevant DOM elements as needed.
But, how does AngularJS know which actions to perform when? If you start to test the rendering, you'll discover that AngularJS doesn't brute force DOM creation; that is, it doesn't recreate the DOM for every rendering. Instead, it only creates a new DOM element when a completely new item has been introduced to the collection. If an existing item has been updated, AngularJS merely updates the relevant DOM properties rather than creating a new DOM node.
This can still impact performance unnecessarily, i.e. when passing elements by-value in a collection (there's an excellent example of this in the blog post linked above). That's why Angular supports "track by" for ngRepeat since version 1.2: It's a way to help Angular decide when DOM creation is necessary:
With this association in place, AngularJS will not $destroy and re-create DOM nodes unnecessarily. This can have a huge performance and user experience benefit.
The official documentation states:
You can also provide an optional tracking function which can be used to associate the objects in the collection with the DOM elements. If no tracking function is specified the ng-repeat associates elements by identity in the collection.
For example: item in items track by item.id is a typical pattern when the items come from the database. In this case the object identity does not matter. Two objects are considered equivalent as long as their id property is same.

Model bindings not working on checkboxes in a nested loop of a directive

I am creating a grid of checkboxes to enable a user to select different events and age classes for an athletics meeting. The events are the columns, the classes are the rows.
Here's the plunk: http://plnkr.co/edit/j6gRR18qXCDNCMQ9VDNG?p=preview
I first created this with an nested ng-repeat. It works, but is very slow to load which is apparently due to the creation of data bindings and watches. To speed things up, I wrote a directive to do the nested looping and the building of the html.
The checkboxes are bound to a two dimensional array - classevent[][].
The problem is that I cannot get the bindings to work with the directive.
Here are the steps to see the problem in Plunker:
When it finally loads, open the console and then scroll down the grid to the bottom - column 201, row 32. Click save and you get false in the console. Check the box 201,32 and click save again. It's true. So the data binding is working.
Now go to the html and comment out the tbody.../tbody and uncomment the tbody classeventgrid.../tbody directive as described in the code.
It loads much faster, but if you do the same as before, you will see that the bindings are broken.
Can anyone tell me what I have done wrong?

Simple One way binding for ng-repeat?

I have read some articles that said ng-repeat would led to poor performance if there is over 2000 items, because there are too many two way binding to watch. I am new to angularjs and have trouble understanding the relationship between ng-repeat and two-way binding:
Does ng-repeat (like outputting a list of json objects) necessarily create two way binding?
Is there a simple way to do ng-repeat using only one way binding? (preferably do not need external module)
Like user1843640 mentioned, if you are on Angular 1.3, you can use one-time-binding, but, just for clarity, you need to put the :: on all the bindings, not just the repeater. The docs say do this:
<div ng-repeat="item in ::items">{{item.name}}</div>
But, if I count the watchers, this only removed one. To really drop the number of two-way-bindings, place the :: on the bindings within the repeater, like this:
<div ng-repeat="item in ::items">{{::item.name}}</div>
Here are two plunkers that will display the number of watchers:
All Bindings
Repeater Only
Thanks goes out to Miraage for provinding the function to count the watchers https://stackoverflow.com/a/23470578/2200446
For anyone using or upgrading to Angular 1.3 you can now use "one-time binding". For ng-repeat it would look like this:
<div ng-repeat="item in ::items">{{item.name}}</div>
Notice the ::items syntax.
For more information check the Angular documentation for expressions.
This blog post presents some interesting solutions. The end result was:
Upgrade to AngularJS 1.1.5 and use limitTo together with Infinite scrolling. AngularJS ng-repeat offers from version 1.1.4 the limitTo option. I slightly adapted the Infinite Scroll directive to make scrolling within a container possible that does not have height 100% of window.
Basically you limit the number of objects you initially render, then use the Infinite scrolling directive to render more as needed. Since you don't want an external module, just mimic the infinite scroll functionality as needed with your own script.
Note: This should solve your performance problem but it won't remove two-way binding.
what ever is generated by ng-model will be having a watcher on data(model) which reduces the page performance if it crosses 200 watchers.
Refer this for ONE WAY BINDING http://blog.scalyr.com/2013/10/31/angularjs-1200ms-to-35ms/ but make sure you use it properly
Hope it helps!!!

Resources