I'm rendering a calendar UI with AngularJS and I'm running into some pretty big performance issues when flipping through the weeks. Let me explain.
The UI looks something like this:
I loop over all the persons, then for each person I loop over the days, and then render the calendar objects for that user for that day. Something like this (simplified):
<div ng-repeat="user in ::ctrl.users track by user.id" class="row">
<div ng-repeat="day in ctrl.days" class="cell">
<div ng-repeat="item in ctrl.items[user.id][day] track by item.id">
<div class="item">
There are not a crazy amount of watcher on this page (about 500), almost everything is bind-once.
The problem is when the user clicks the prev/next buttons to load the previous or next week. This changes the ctrl.days array with new days, and all the correct items are loaded. This performs fine until you have a ton of people and calendar items. Then all the destroying and recreating of DOM elements is really slow.
I came across the sly-repeat directive which is meant to cache and reuse DOM elements but because my outer ng-repeat changes (ctrl.days), the inner ng-repeat (with the items) is also recreated. So it doesn't really work.
How can I solve this problem? Right now browsing through the weeks with a large data-set takes about 2 seconds, which is of course not acceptable. With a small set of users and calendar items everything is super snappy.
Try using track by in your nested ng-repeats. This will prevent them from reloading unnecessary inner repeats. For more information, see:
http://www.codelord.net/2014/04/15/improving-ng-repeat-performance-with-track-by/
Related
HTML:
{{vm.regions}}
<div ng-repeat="region in vm.regions">
{{region}}
</div>
On button presses, the model a.k.a vm.regions gets updated in the controller. For example vm.regions = [].
I can see that the array {{vm.regions}} is instantly updated, but the elements in the div take at least a second to update, meaning you can see old elements for a bit in the newly updated list, for example.
What is causing this?
It is in AngularJs Best Practices to always add "track by $index" in order to inprove performances.
{{vm.regions}}
<div ng-repeat="region in vm.regions track by $index">
{{region}}
</div>
Link : https://docs.angularjs.org/api/ng/directive/ngRepeat
Try using vs-repeat. It applies virtual scrolling to ng-repeat which increases its performance drastically even if you are not using one time binding.
http://kamilkp.github.io/angular-vs-repeat/#?tab=8
I have webpage that has paging for an outer loop of elements, that when clicked will expand, make a call to the server, then display another table filled with paginated rows. I only experience this hang in IE which I have come to find seems like a common problem.
The outer results set will only display 25 rows at a time. The inner will also only display 25 rows at a time. Only one accordian panel can be opened at a time. I am using track by and one time bindings. The repeat-starts both have corresponding repeat-ends. The outer result set loads very quickly with no hangs.
I am using angularjs 1.5.8.
Some sample snippets of html:
<div class="accordionHdr" ng-repeat-start="(index, obj1) in ::vm.obj1Array track by $index">
//normal accordian panel stuff here. clicking a panel calls server to get inner results
//normal table code
<tr ng-repeat-start="(index2, obj2) in ::vm.obj2Array track by $index">
<td>several cells in here</td>
</tr>
</div>
What can I do to eliminate this hang that only exist in IE? Thanks in advance
I use quick-ng-repeat Quick-ng-repeat for my list to iterate because it is a huge list and I would improve performance.
Now I recognized that if I change the model, the view is not updated with this code:
<span quick-ng-repeat="item in collection track by $index" quick-repeat-list="items">
{{ item }}
</span>
With this code
<span ng-repeat="item in collection track by $index">
{{ item }}
</span>
every works fine.
Does anyone have any idee why this happens?
Thanks a lot!
quick-ng-repeat does not implement deep watch just like ng-repeat, it implement one way binding approach. So if your model changes frequently, don't use it.
In Angular 1.2 a new addition was made to the syntax of ngRepeat: the amazingly awesome track by clause. It allows you to specify your own key for ngRepeat to identify objects by, instead of just generating unique IDs.
This means that you can change the above to be ng-repeat="task in tasks track by task.id" and since the ID would be the same in both your original tasks and the updated ones from the server – ngRepeat will know not to recreate the DOM elements and reuse them
The Quick ng-repeat is doing one way binding ,it something similar to what we do in angularjs
<div ng-repeat="item in ::items">{{item.name}}</div>
This does not create unnecessary watches.
I think Quick-ng-repeat use Single binding same as in angular 1.4 like (::).Single binding mean it will be create a watcher for your repeat that why it will not effect when object is change.
<div ng-repeat="obj in ::objList">{{obj.name}}</div>
I have a problem with angular ng-repeat directive.
Currently I work on some project where from the API I get a list of items (some times it could be 1k items) and this list should be refreshed every 5 seconds (it is monitoring related project).
When the list length is a little bigger the website while re-rendering DOM could "slow". It comes out that angular regenerate the whole DOM (but 95% of item are the same ! )
One of the possible approach is to set "track by" expression for example to item.id. But here comes another problem, I also want regenerate items when for example descriptions was changed by other user. Since track by is expression to item.id changes in item.description didn't update item in DOM.
There is way to track by over multiple properties? Maybe some function?
Or maybe do comparison by "hand" ?
Any ideas, code samples I would appreciate :)
UPDATE
what I discover when I set track by to item.id angular didn't re-crete html for items, just update value in already created element and it seems to be "faster" then removing and creating.
Previously I though a little bit different.
FIX
For those who are looking for better performance over >1k items in ng-repeat USE track by item.id it will boost your performance ;)
You do not need to create a function to handle track by multi properties.
You can do:
<div ng-repeat="item in lines track by item.id+item.description">
As the comment suggested you could try something like this:
<select ng-model="item" ng-options="item.id as item.description for item in items track by itemTracker(item)">
In your controller:
$scope.itemTracker= function(item) {
return item.id + '-' + item.description;
}
This might help with the number of DOM elements being re-rendered when the list changes.
Based my knowledge, the angularjs model is bind to the ui view, so the model will rerender via $apply or $digest once the value changed. so in your case, u wan bind the model value to ui view but also do not want to re-render the view if the value has not change,that is impossbile. this is what i know.
however, u can just manipulate the dom element. for example
store the data to a variable
var x = [{id:"id1",value:"v1"},{id:"id2",value:"v2"}]
in html, manual append or using directive to append, then assign the id to the element,
<div id="id1">v1</div>
check and compare the value, based ur needs.
once found, then angular.element("#yourid").text()
this will solve your browser resources consume problems.
I was faced the performance issue of ng-repeat directive, I rendered the PDF formFields using ng-repeat directive, somehow it halts my browser. But when I use track by $index and limitTo:1 together in same ng-repeat, it works fine and it enhances the rendering speed. I can't understand the logic behind this but it works extraordinary fast :)
This works faster and fine for me, also limitTo:1 not work, some how it is binding the limit with $index and halting of browser issue fixed.
<div ng-repeat="friend in friends track by $index | limitTo:1">
{{ friend.id }} — {{ friend.name }}
</div>
if we use limitTo before track by $index than it behaves normally the limit behavior of angular js which is understandable
<div ng-repeat="friend in friends | limitTo:1 track by $index">
{{ friend.id }} — {{ friend.name }}
</div>
OR
<div ng-repeat="friend in friends | limitTo:1">
{{ friend.id }} — {{ friend.name }}
</div>
Although i achieve my performance goal, but i want to know the logic behind this.
Here is the link of jsfiddle
http://jsfiddle.net/neglingeyes/G6q84/
You can not measure multiple ng-repeat directives at once with post-repeat.
This is because the way it tries to figure out when the rendering is complete is to set a timeout function for the next event loop tick, and that really means all the directives on the page have finished rendering, not just this specific ng-repeat.
In your demo fiddle, I guess each next post-repeat timeout gets processed in a separate event loop tick or something like that, and this is why their "measured" performance seems to get worse and worse. If you changed their order, again the first will seem to be the fastest. But only by a couple of ms - a trivial difference.
Have you tried to benchmark each case separately? Also, on what platform and with what data? On desktop browsers and with such short data it is too fast to make a difference either way.
As to what the first code segment does - it is parsed as friend in friends track by ($index | limitTo:1) ("What brackets?" ;-) )
The limitTo filter can only be applied to arrays, not to scalar variables like the number $index. But the angular filters normally return their input unchanged if they can not make sense of it, so I guess that is what happens here too.
Here is an explanation:
by default ng-repeat creates a dom node for each item and destroys that dom node when the item is removed; As it watches the ng-repeat object, it expects the object to change & implement the change & hence keep adding or deleting nodes. But using $index it reuses DOM nodes.
Here is the link where I picked up this information- http://www.reddit.com/r/angularjs/comments/2cvo16/speeding_up_angularjs_with_simple_optimizations/