AngularJS : nested ng-repeat peformance - javascript

I have a requirement where data is dynamic and very hierarchical (3- 4 level deep) where each inner level can be a list which can again contain list (please look at my template below). Everything looks good in terms of performance upto 2 ng-repeat but performance degrade sharply for level 3 onwards ng-repeat.
<div class="struct-property">
<div ng-repeat="structGroup in triple.properties track by $index">
<div ng-repeat="props in structGroup track by $index">
<simpleproperty triple="props"></simpleproperty>
<multiplevalueproperty triple="props"></multiplevalueproperty>
<div ng-if="props.isStructProperty">
<div class="struct-property">
......
<div ng-repeat="structGroupInner in props.properties track by $index">
<div ng-repeat="propsInner in structGroupInner track by $index ">
<simpleproperty triple="propsInner"></simpleproperty>
<multiplevalueproperty triple="propsInner"> </multiplevalueproperty>
</div>
</div>
</div>
</div>
Please Note: that I have only static data
I used "track by" but not sure if I used it correctly. I did tried to log if DOM is getting recreated with children ng-repeat and I do see that with every nested ng-repeat, parent DOM is getting regenerated and thats the reason performance is very bad.
I also tried to refactor my data so that I can avoid nested ng-repeat but unfortunately I cannot refactor it further because it as a RDF-JSON representation.
Thanks in advance!

Related

How can I isolate/encapsulate $index in Angular using 2 ng-repeat directives on the same page?

I have a website built with Angular 1.4 with 2 slideshows. One in the header and one for a partner logo slider. They are in the same view when all it said and done.
Header
<div layout="row">
<div flex="100" flex-gt-sm="70" hide-xs class="header-slider center">
<img
ng-repeat="i in slides"
class="header-slide-animation"
src="{{i.img}}"
ng-hide="!isCurrentSlideIndex($index)"
ng-class="{'active':isCurrentSlideIndex($index)}"/>
</div>
And then here's the logo slider
<div class="partners-slider center">
<a ng-repeat="i in slides"
class="slide-animation"
href="{{i.href}}"
target="_blank"
ng-hide="!isCurrentSlideIndex($index)"
ng-class="{'active':isCurrentSlideIndex($index)}">
<img src="{{i.img}}" alt="{{i.title}}" />
</a>
I'm following this tutorial here http://onehungrymind.com/build-sweet-photo-slider-angularjs-animate/ and it uses TweenMax animate the sides.
Everything was working great with one slide show on the page but I have each split into separate directives being included on the same page and the problem is that $index is conflicting. The partner logo animation moves faster than the header slide show and also has a larger array so you can imagine the problem when the header's $index gets overwritten by the other and is out of range, etc... What can I do to isolate $index so the two slideshows don't step on each other?
It's not $index that is the problem, it is the shared scope whereby each directive is sharing the parent controller scope.
$index is actually isolated as it only exists in each child scope of ng-repeat
So whatever isCurrentSlideIndex($index) is doing in controller will be shared by both instances
The simple solution is to use an isolated scope in directive so both are separate instances.
Just move the methods used in controller to directive. You can then use one attribute in directive to receive the images in the isolated scope from the parent controller
Alright I found an article that helped me realize a solution to the problem. http://codeutopia.net/blog/2014/11/10/angularjs-best-practices-avoid-using-ng-repeats-index/
Overall the moral of the story is not to use $index because it can be problematic. I chose to modify the view to pass the image object and check for that against the current index.
HTML
<div layout="row">
<div flex="100" flex-gt-sm="70" hide-xs class="header-slider center">
<img
ng-repeat="j in headerSlides"
class="header-slide-animation"
src="{{j.img}}"
ng-hide="!isCurrentHeaderSlide(j)"
ng-class="{'active':isCurrentHeaderSlide(j)}"/>
</div>
This is what the JavaScript was
$scope.isCurrentSlideIndex = function (image) {
return $scope.currentIndex === index;
};
This is what I changed it to
$scope.isCurrentHeaderSlide = function (slide) {
return $scope.headerSlides[$scope.currentIndex] == slide;
};
And it works :-)

track by $index isn't solving dupes error

I am trying to iterate through an array of objects and display them in my HTML using Angular. I have done this before without any problems by using "track by $index". But the standard error is still raised:
Error:
[ngRepeat:dupes] http://errors.angularjs.org/1.3.15/ngRepeat/dupes?p0=(key%2C%20value)%20in%20n&p1=string%3Ab&p2=b
But I have already added track by $index. I am confident there isn't any issue requesting the data as I have been able to display it correctly.
<div ng-repeat="n in post.userData.bookmarks track by $index" >
<div ng-repeat="(key, value) in n">
<div>{{ key }}</div>
</div>
</div>
Suggestions?
Dataset:
bookmarks: [{"1234": "Title1"}, {"5678": "Title2"}]
What happens is that a user bookmarks a post that they want to read later. I save the post id and the title of the post to an object and store this in an array. (I actually just wanted to store it to an object like so: {"1234": "Title1", "5678": "Title2"}, but couldn't figure it out with mongodb, since it doesn't seem to let you save as object data type.)
Then I want to ng-repeat the user's bookmarks in their profile. The only way I know how to do this is to iterate through the array and then iterate through each object. Of course, there is only one item in each object. But this is why I have the second ng-repeat. But when I do track by $index for both, as one of the answers lists below, it still doesn't give the desired behavior.
Something that could be causing a problem is that I also have a sort of dashboard that can be viewed on the side while viewing any given post. And on the dashboard they should be able to see their bookmarks. Also if they are on a post that they have bookmarked, if they click a (un)bookmark button on the post, it should automatically remove that bookmark on their bookmark dashboard.
But none of the suggestions have worked.
You forgot another ng-repeat: <div ng-repeat="(key, value) in n track by $index">
I don't know Why u write two times repeat
<div ng-repeat="n in post.userData.bookmarks track by $index" >
<a ng-href="/tuts/{{n.key}}">{{ n.key}}</a>
</div>
OR
<div ng-repeat="(key,value) in post.userData.bookmarks track by $index" >
<a ng-href="/tuts/{{value}}">{{ value}}</a>
</div>
key is anything which you get from object.

How to give continuous indexing for consecutive ng-repeat elements in angular

I am creating some div elements using multiple ng-repeat. Number of ng-repeat elements are dynamic.I want to give continuous indexing for all elements.How is it done in angular way?
like
<div ng-repeat="item in [1,2,1,5]">{{$index+1}}_{{item}}</div>
<div ng-repeat="item in [5,2]">{{$index+1}}_{{item}}</div>
<div ng-repeat="item in [4,6]">{{$index+1}}_{{item}}</div>
Expected result
<div>1_1</div>
<div>2_2</div>
<div>3_1</div>
<div>4_5</div>
<div>5_5</div>
<div>6_2</div>
<div>7_4</div>
<div>8_6</div>
This will work although it is dodgy. 'profile' is used to represent an object to avoid isolated scope. You can write the directive with this logic.
<span ng-init="profile.count = 0">{{profile.count}}</span>
<div ng-repeat="item in [1,2,1,5] track by $index"
ng-init="count=profile.count+1;profile.count=profile.count+1">{{::count}}_{{item}}</div>
<div ng-repeat="item in [5,2] track by $index"
ng-init="count=profile.count+1;profile.count=profile.count+1">{{::count}}_{{item}}</div>
<div ng-repeat="item in [4,6] track by $index"
ng-init="count=profile.count +1;profile.count=profile.count+1">{{::count}}_{{item}}</div>

Angular: one-time binding, recompiling of nested ng-repeat

I am using one-time binding and kcd-recompile, but have a problem with nested ng-repeat. Let's assume the code looks like this:
<div ng-repeat="person in ::people">
<div ng-repeat="friend in ::person.friends" kcd-recompile="person.friends">
{{friend.name}}
</div>
</div>
If I add a friend to one of the people now, I want this person div to be recompiled without recompiling the other people's divs. The problem is: person.friends is only updated, if person is updated.
So it works if I add the kcd-recompile to the first ng-repeat, but then every person's div is recompiled (which I want to prevent).
Is there any possibility to update person without recompiling the whole first ng-repeat?
Found a solution on my own (after hours), if anyone needs it:
Using people[$index].friends instead of person.friends, it works!
<div ng-repeat="person in ::people">
<div ng-repeat="friend in people[$index].friends" kcd-recompile="person.friends">
{{friend.name}}
</div>
</div>

data-binding not working on nested ng-repeat

When nesting ng-repeat it seems that clicking on the toggle button updates that addon in EVERY 'pair' (the first ng-repeat). Can anyone explain to me why this is the case and what I can do to fix it? Check the link above for the custom directive code...
<div ng-repeat="pair in pairs track by $index">
<h3>Pair {{ $index + 1}}</h3>
<div class='fieldrow'>
<button ng-repeat="addon in addons track by $index" toggle-button="addon.added">{{addon.name}} (£{{addon.price}})</button>
</div>
</div>
What is pair and what is addon, and how are they related. Right now, even though you have a nested ng-repeat, you have completely independent data structures, so addons array is same for each pair. Trott.
Based on this insight, I changed ng-repeat="addon in addons track by $index" to ng-repeat="addon in pair.addons track by $index"
Problem solved, thanks!
I think your code does not have any problem. see plunker here.
this code is working well:
<div ng-repeat="pair in pairs track by $index">
<h3>Pair {{ $index + 1}}</h3>
<div class='fieldrow'>
<button ng-repeat="addon in addons track by $index" toggle-button="addon.added">{{addon.name}} (£{{addon.price}})</button>
</div>
</div>

Categories

Resources