Angular vs-repeat performance issue with tables with 7k rows - angularjs

I am using angular vs-repeat to render around 7k rows in a table. If I use vs-repeat for table body, the rendering was very slow.
<table class="table">
<tbody vs-repeat style="width: 100%;">
<tr ng-repeat="row in list track by $index">
<td>{{::row[listColumns[0].colName]}}</td>
<td>{{::row[listColumns[1].colName]}}</td>
<td>{{::row[listColumns[2].colName]}}</td>
<td>{{::row[listColumns[3].colName]}}</td>
<td>{{::row[listColumns[4].colName]}}</td>
</tr>
</tbody>
If I use vs-repeat without tables, in my case I used divs. Its rendering very fast
<div vs-repeat class="table-body">
<div class="row" ng-repeat="row in list track by $index">
<div class="col-md-4">{{::row[listColumns[0].colName]}}</div>
<div class="col-md-2">{{::row[listColumns[1].colName]}}</div>
<div class="col-md-2">{{::row[listColumns[2].colName]}}</div>
<div class="col-md-2">{{::row[listColumns[3].colName]}}</div>
<div class="col-md-2">{{::row[listColumns[4].colName]}}</div>
</div>
</div>
Please suggest how to improve performance for table?

With vast collections of items that render many DOM elements, even the most negligible processing of items inside an ng-repeat can choke your app.
DOM-related solutions that check if elements are in view don't tell you if nested components have been loaded and rendered completely, leading to errors.
My solution to this problem is angular's limitTo : limit: begin filter, along with iScroll or native scrollTop and scrollBottom callbacks, which raise or reduce the begin index in the controller according to the browse direction - sort of frontend pagination where only a specific number of items are shown at the DOM.
<tr ng-repeat="item in items | limitTo : 100 : 600 track by $index">
<span>{{ item.property }}</span>
</tr>
Then you can find the optimal limitTo number that work out best for the app - while keeping it light, quick, and agile as Angular should be.

use ng-repeat on small set of an array. Do pagination or lazy loading. store data to temporary array which you want to display rather having n-repeat on 7k records.
Thoughts?
Thanks!

Related

How to get number of objects being watched below 2000 of angular UI grid

How to implement below mentioned points to improve performance of UI grid
Avoid using ng-repeat (sort it before insertions)
Use of one-time bindings (:: syntax)
Is there a way by which i can reduce $watches count in UI grid component?
you can use something like below to reduce the watchers count in application :
<table border="solid 1px">
<tr ng-repeat="dept in ::departments">
<td>{{::dept.name}}</td>
<td>{{::dept.title}}</td>
<td>{{::dept.retention}}</td>
</tr>
</table>

angular only generate part when actually shown

I am using angular to show render a bigger dataset. In data.data there are several keys and to each key corresponds an array with around 3000 lines. When the user first open the page all of showraw[key]-s are set to false, so they actuall on get a list of the possible keys in panel-heading-s and when they click on the heading is when they actually get to see the data. Before I added this part the page loaded very fast, now it takes considerable time (few seconds, and it is not because of fetching the data, as the whole dataset was fetched previously also, I just didn't show it all). As far as I can tell, angular basically renders everything even though its not shown (although I guess it might not store it, because every time I click one of the heading to show the data, it still takes a second or two for it to appear).
<div class="panel panel-default">
<div class="panel-heading"><h2>Raw data</h2></div>
<span ng-repeat="(key,val) in data.data">
<div class="panel-heading" ng-click="showraw[key] = ! showraw[key]"><b>{{key}}</b></div>
<table class="table table-striped table-hover table-responsive" ng-show="showraw[key]">
<tbody>
<tr ng-repeat="line in val track by $index" ><td ng-repeat="l in line.split(' ') track by $index">{{l}} </td></tr>
</tbody>
</table>
</span>
</div>
My question is the following: what would be the best practice in speeding up page loads? I do not wish to make the user wait for rendering of data that is not shown to her.

When does duplicate digest computations warrant redesign in AngularJS?

I have been working with AngularJS for a while now and this is one subject I still am not sure about. Let me start with an example. In my site I have a table with data. I want to be able to search/sort/filter this data in a number of ways. We have a search box that utilizes the built in "filter" filter to search the data. We also have a number of other filters that allows the user to check checkboxes and get different views (think Amazon).
The issue is because things like pagination need to know about these filters and must apply its computations based off the filtered view of data, not the original data set.
Doing so makes our HTML look like this:
<div class="row paginationInfo clearfix">
<div class="col-xs-3 showResultsWrap">
<span data-name="summary"><b>{{ getPageStart2((model.IndividualMailboxes.filtered | pipe:model.IndividualMailboxes.filters.functions:model.IndividualMailboxes.filters.values), model.IndividualMailboxes.currentPage, model.IndividualMailboxes.resultsPerPage) }} - {{ getPageEnd2((model.IndividualMailboxes.filtered | pipe:model.IndividualMailboxes.filters.functions:model.IndividualMailboxes.filters.values), model.IndividualMailboxes.currentPage, model.IndividualMailboxes.resultsPerPage) }}</b> of <b>{{ (model.IndividualMailboxes.filtered | pipe:model.IndividualMailboxes.filters.functions:model.IndividualMailboxes.filters.values).length }}</b></span>
</div>
<div class="col-xs-6 text-xs-center">
<div max-size="5" boundary-links="true" first-text="«" last-text="»" previous-text="‹" next-text="›" uib-pagination ng-show="(model.IndividualMailboxes.filtered | pipe:model.IndividualMailboxes.filters.functions:model.IndividualMailboxes.filters.values).length > 10" total-items="(model.IndividualMailboxes.filtered | pipe:model.IndividualMailboxes.filters.functions:model.IndividualMailboxes.filters.values).length" ng-model="model.IndividualMailboxes.currentPage" items-per-page="model.IndividualMailboxes.resultsPerPage" class="pagination-md"></div>
</div>
<div class="col-xs-3 resultsPerPageWrap form-inline text-xs-right">
<span>View</span>
<select sk-id-gen="resultsPerPage" name="ResultsPerPage" class="form-control m-l-1" ng-model="model.IndividualMailboxes.resultsPerPage" ng-options="val as val for val in [10, 20, 50, 100]">
</select>
</div>
</div>
The important thing to notice here is how many times we have to do (hereIsMyArray | pipe:param1:param2).length over and over again for the pagination to work.
So correct me if I am wrong, but I believe every digest cycle we are running these pipes/filters 5+ times. This is probably OK with a small dataSet, but if I start having to filter 10,000 items then I'm sure I will see performance issues.
It would be much more efficient if we only had to do the pipe/filtering 1 time and each of these HTML components could just use that single instance of the filtered data set.
The issue with not doing it directly in the HTML is you no longer have automatic watches setup to see when data changes and updates the view.
If any of my statements are wrong please let me know. I am curious if anyone has had this issue before and has a solution?
Thanks

Angular JS Nested ng-repeat for bootstrap badges

Im trying to tag different video items in a bootstrap table. I'm already using an ng-repeat to loop over the array of objects which store video data. I'm now trying to create a nested ng-repeat to loop over another array of "tags" within the ng-repeat that creates each table row.
I'm getting some weird results though. I was hoping that I could just put an ng-repeat on the td and then put the angular expression in a span with the boostrap class "badge". Any thoughts as to whats going wrong?
http://jsfiddle.net/jheimpel/6nh100ca/
<tr ng-repeat="topic in topics">
<td><a ng-href="#/{{topic.url}}"><i class="fa fa-play"></i></a></td>
<td>{{topic.topic}}</td>
<td>{{topic.date}}</td>
<td>{{topic.presenter}}</td>
<td ng-repeat="tag in topic.tags">
<span class="badge">{{topic.tags}}</span>
</td>
</tr>
Change it to
<span class="badge" ng-bind="tag"></span>
You're telling it to print out the array you're ng-repeating instead of the object the ng-repeat is giving you. Also ng-bind is better, see this

Conditionally adding elements in ng-repeat block

Is it possible to add stuff to the markup based on condition?
Like in this example, when I want to add td only on the first iteration (only for the first element of myData)?
<tr ng-repeat="m in myData">
<td>{{m.Name}}</td>
<td>{{m.LastName}}</td>
#if myData.indexOf(m) = 0 then // something like that
<td rowspan ="{{m.length}}">
<ul>
<li ng-repeat="d in days">
{{d.hours}}
</li>
</ul>
</td>
</tr>
Yes, AngularJS has 2 directives for this occasion:
The ng-show / ng-hide family of directives can be used to hide (by
using display CSS rules) parts of the DOM three based on a result of
evaluating an expression.
If we want to physically remove / add parts of the DOM conditionally
the family of ng-switch directives (ng-switch, ng-switch-when,
ng-switch-default) will come handy.
At the end of the day both solutions will give the same visual effect but the underlying DOM structure will be different. For simple use cases ng-show / ng-hide is probably OK, but larger portions of the DOM should be treated with ng-switch.
For the use case from this question I would advice using ng-switch.
The best solution should be:
<tr ng-repeat="m in myData">
<td>{{m.Name}}</td>
<td>{{m.LastName}}</td>
<td ng-if="$first" rowspan="{{myData.length}}">
<ul>
<li ng-repeat="d in days">
{{d.hours}}
</li>
</ul>
</td>
</tr>

Resources