ng-repeat takes too much time to render data - javascript

I know there are many questions already posted for the same issue but none of the solutions work in my case.
On calling a web service I get JSON response. In this JSON, there are around 2000+ objects from which I need to display data on the table. I want to display all (2000+) records in the table and Yes, I cannot limit or paginate, need to display it on a single page (I know it's stupid but it's the business requirement). I don't need sorting or searching.
Data transfer is about 2MB and the request completes in about 2-4 secs approx. but it takes around 10-15 secs to data render on the page.
Now, what I am looking for is either speed ng-repeat binding things up (if possible) or display the data as soon as I receive it and keep on adding it until all rows are displayed.
Check out the code below :
HTML
<table class="table table-bordered table-striped cf">
<thead style="color: #333;">
<tr>
<td>Asset Name</td>
<td>Date/ Time</td>
<td>Location</td>
<td>Ignition</td>
<td>Speed</td>
<td>Heading</td>
<td>Direction</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="cols in tableData">
<td>{{ cols.aN }}</td>
<td>{{ cols.dT }}</td>
<td>{{ cols.Lat }}, {{ cols.Lon }}</td>
<td>{{ cols.I }}</td>
<td>{{ cols.S }}</td>
<td>{{ cols.H }}</td>
<td>{{ cols.D }}</td>
</tr>
</tbody>
</table>
JS
var ignition_text = '';
var lat = '';
var lon = '';
for (var i = 0; i < data.length; i++) {
if (data[i].ignition = 1) {
ignition_text = "On";
} else {
ignition_text = "Off";
}
$scope.$apply(function() {
$scope.tableData.push({
aN: name,
dT: data[i].eventUTCTime,
Lat: data[i].latitudeDegrees,
Lon: data[i].longitudeDegrees,
I: ignition_text,
S: data[i].speedMPH,
H: data[i].longitudeDegrees,
D: data[i].latitudeDegrees
});
});
}
Thanks in advance!

You probably wont need $scope.$apply at all. And even if you need it, you should only use it once you pushed all data to the table. Otherwise, every added entry will force an digest-cycle. Just build your array and assign the finished array to the scope-variable. Then angular will only build the table once.
Depending on the nature of your variable name you may be able to eliminate the array building as well and just use the data you are downloading. Apart from nameyou just use that data anyway.

Here is a plunk that has a similar data size but loads much faster http://plnkr.co/edit/I4rN1ZMaR3e1mbcsJ9Ka. If you were to make a quick plunk I could use your data and edit your code but from the looks you just need the main assignment to the scope without the apply for the data and add a track by to the ng-repeat. SN: You would want to manipulate your data inside the for loop then do the assignment to the scope.
for (var i = 0; i < data.length; i++) {
if (data[i].ignition = 1) {
ignition_text = "On";
} else {
ignition_text = "Off";
}
}
$scope.tableData=data;
JS
$http.get("largeData.json").then(function(response) {
vm.test = response.data;
});
HTML
<tbody>
<tr ng-repeat="(key, value) in main.test track by $index ">
<td>{{ value.ask }}</td>
<td>{{ value.bid }}</td>
<td>{{ value.volume_btc }}, {{ value.volume_percent }}</td>
<td>{{ value.last }}</td>
<td>{{ value.timestamp }}</td>
</tr>
</tbody>

Related

split array elements from python into separate HTML tables based on specified attribute

I have a Drilling table containing, daily drilling and all time drilling record. we have project in which drill and the corresponding client in each record. now i am building a report in which i need to view a daily summary of all drilling PER Client rather. More precisely i need to see a list where i have client name, followed by the number of drilling records for that client (any number), then the total of the meters that were drilled.
In my code i get a filter based on day then client, after that i get the sum for all drilling of a particular client. all the data are correct and and i am getting all record for that day with their client. I put them all in arrays and send to HTML template. when i send it to HTML they all get in the table but formated in only one table row...my problem is how can i best format it the way i explained at the beginning of the question
this the view:
def report(request):
today = date.today()
clt_ = []
clt_day_t_m_ = []
result = []
client = []
drill_date = []
auger = []
vehicle = []
project_name = []
shift = []
meters_drilled = []
targetmeters = []
driller = []
comment = []
m_p_obj_day = Drilling.objects.filter(drilling_date=today)
_total = m_p_obj_day.aggregate(Sum('ms_drilled'))['ms_drilled__sum']
for ma_por_obj_day in m_p_obj_day:
client_ma_por_obj_day = Client.objects.filter(clt_nme=ma_por_obj_day.client)
for clt_s in client_ma_por_obj_day:
clt_.append(clt_s.clt_nme)
clt_s_=list(set(list(OrderedDict.fromkeys(clt_))))
_items = ""
for f in clt_s_:#
_client_obj = Client.objects.filter(clt_name=f)
_items = m_p_obj_day.filter(client=_client_obj)
for i in _items:
client.append(i.clt)
drill_date.append(i.drill_dte)
auger.append(i.auger)
vehicle.append(i.vehicle)
project_name.append(i.prjt_nme)
shift.append(i.shift)
meters_drilled.append(i.mtrs_drilled)
targetmeters.append(i.targetmeters_drilled)
driller.append(i.driller)
comment.append(i.comment)
ut_ = m_p_obj_day.filter(client=_client_obj).aggregate(Sum('ms_drilled'))['ms_drilled__sum']
clt_day_t_m_.append(u)
client_ = "\n".join(map(str, client))
drill_date_ = "\n".join(map(str, drill_date))
vehicle_ = "\n".join(map(str, vehicle))
auger_ = "\n".join(map(str, auger))
project_name_ = "\n".join(map(str, project_name))
shift_ = "\n".join(map(str, shift))
meters_drilled_ = "\n".join(map(str, meters_drilled))
targetmeters_drilled_ = "\n".join(map(str, targetmeters))
driller_ = "\n ".join(map(str, driller))
comment_ = "\n".join(map(str, comment))
clt_d_total_m_ = "\n".join(map(str, clt_day_t_m_))
context = {
"_total": _total,
"_items": _items,
"m_p_obj_day": m_p_obj_day,
"clt_s_": clt_s_,
"clt_d_total_m_": clt_d_total_m_,
"client_": client_,
"drill_date_": drill_date_,
"auger_": auger_,
"vehicle_": vehicle_,
"project_name_": project_name_,
"shift_": shift_,
"meters_drilled_": meters_drilled_,
"targetmeters": targetmeters,
"driller_": driller_,
"comment_": comment_,
}
return render(request, "jourly_report.html", context)
This is sample of a template portion:
<thead>
<th>Client</th>
<th>Drill Date</th>
<th>Auger</th>
<th>Vehicle km</th>
<th>Project Name</th>
<th>Shift</th>
<th>Meters Drilled</th>
<th>Target meters</th>
<th>Driller</th>
<th>Comment</th>
<th>Total for Each Client</th>
</thead>
<tbody>
{% for all_item in all_items %}
<tr>
<td>{{ client_ | linebreaks }}</td>
<td>{{ drill_date_ | linebreaks }}</td>
<td>{{ auger_ | linebreaks }}</td>
<td>{{ vehicle_ | linebreaks }}</td>
<td>{{ project_name_ | linebreaks}}</td>
<td>{{ shift_ | linebreaks }}</td>
<td>{{ meters_drilled_ | linebreaks }}</td>
<td>{{ targetmeters_ | linebreaks }}</td>
<td>{{ driller_ | linebreaks }}</td>
<td>{{ comment_ | linebreaks }}</td>
<td style="font-size: 13px!important; font-weight: bold!important; background-color: #BDBDBD;">{{ client_day_total_m_ | linebreaks }}
</td>
</tr>
{% endfor %}
<thead >
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th>Grand Total Meters Drilled: </th>
<th style="font-size: 14px!important; font-weight: bold!important; background-color: #FA5858;">{{ _total }}</th>
</thead>
as you can see on the image all the drillings are all in the same column or data cell. they are not in differents rows. i want any suggestion to help have access individual items in these array and put them in each of them their rows then create a table for each client where we will put only records of that client int the table (each client has their table)... if i was not clear please let me know.

AngularJS How Can I Ignore hidden table rows in ng-repeat but keep the increment?

I have an ng-repeat that generates a series of table rows. One column is titled "task status" and if the status is displaying "done", I see no reason in showing it, as the job has been completed.
I used ng-show = values != 0; this initially worked until I added an increment to number the tasks.
What I have found was that the data = "done" were not totally removed from the DOM and still regstering in the list disrupting the increment. See image below:
list increment disruption
So the rows 2 and 3 are data that equal "done". What can I do to ignore them?
Here is my markup:
<table class="backlog table table-bordered table-striped" width="100%" border="0" cellpadding="0" cellspacing="0" summary="Our Jira Backlog">
<tbody>
<tr>
<th>Priority</th>
<th>Task Priority Score</th>
<th>Task Summary</th>
<th>Due date</th>
<th>Task Status</th>
</tr>
<tr ng-repeat="issue in issues | orderBy: '-fields.customfield_12401'" ng-if="issue.fields.status.statusCategory.name != 'Done'">
<td>{{ $index + 1 }}</td>
<td>{{ issue.fields.customfield_12401 }}</td>
<td>{{ issue.fields.summary }}</td>
<td>{{ issue.fields.customfield_13700 }}</td>
<td>{{ issue.fields.status.statusCategory.name }}</td>
</tr>
</tbody>
</table>
So anything that comes from "issue.fields.status.statusCategory.name" needs to be ignored so the Priority (First Column) goes, 1,2,3,4,5 etc and not display "done" the Task Status Column.
I think the best way to handle this situation is to the filter the array first. You can filter the array all in the ng-repeat expression, just retain a variable of the filter output that you can reference in the template (if you need to).
Check out the answer to this SO question: AngularJS - how to get an ngRepeat filtered result reference
edit: to clarify I think you should change your ng-if into a custom filter, and apply it before the ng-repeat indexes the filtered array:
<tr ng-repeat="issue in (filteredIssues = (issues | orderBy: '-fields.customfield_12401' | filter: customFilterFunction))">
<td>{{ $index + 1 }}</td>
You should be able to keep your ng-if as a filter and also keep incrementing your $index by simply moving your $index to the ng-repeat as track by:
<tr ng-repeat="issue in (issues | orderBy: '-fields.customfield_12401') track by $index" ng-if="issue.fields.status.statusCategory.name != 'Done'">
Your $index should match the index of the for-each being performed, even with the ng-if implemented.
UPDATE: I believe I interpreted your question wrong. You want your $index to skip you "done" rows? That wasn't clear...
As #Ericson578 suggested, you should make a custom filter to remove the "done" rows from your array.
<tr ng-repeat="issue in ((issues | removeDoneRows) | orderBy: '-fields.customfield_12401') track by $index" >
...and your removeDoneRows filter (please come up with a better name for this):
angular.module('myFilter', [])
.filter('removeDoneRows', function() {
return function(arr) {
var retval = [];
for(var i = 0; i < arr.length; i++){
if(arr[i].fields.status.statusCategory.name.toLowerCase() != "done")
retval[retval.length] = arr[i];
}
return retval;
};
})

Angularjs evauate when data it ready

I am developing my first angular-app with an .Net backend.
I get my data async from a webmethod using a http.post. That all works fine.
Client-side I would like to do some simple calculations (a final row in a table which contains sums of all the data in table)
The code to do this is pretty straight forward but my problem is the data i not ready when I try to do it.
I have read that I could use a promise and a service or a factory. But I am not sure what we be the best way to go.
My code for the view:
<div ng-controller="taskCtrl as ctrl">
<div class="col-md-10 container outer">
<h1 class="center-block">{{ctrl.SprintViewModel.SprintName}}</h1>
<table id="SprintMetaDate">
<tr><td>Projekt:</td><td>{{ctrl.SprintViewModel.ProjektName}}</td></tr>
<tr><td>Periode:</td><td>{{ctrl.SprintViewModel.StartDate}} - {{Ctrl.SprintViewModel.EndDate}}</td></tr>
<tr><td>Udarbejdet af/d:</td><td>{{ctrl.SprintViewModel.MadeBy}}</td></tr>
</table>
<h3>Sprint Resume:</h3>
<br/>
{{ctrl.SprintViewModel.SprintResume}}
<h3>Sprint afslutning:</h3>
{{ctrl.SprintViewModel.SprintDemo}}
<h2>Scope og Økonomi </h2>
<h3>Sprint Opgaver</h3>
<table id="SprintTasks" class="col-md-12">
<tr><th>Opgave</th><th>Estimat</th><th>Forbrug</th><th>Udest.</th><th>*</th><th>Pris (DKK)</th></tr>
<tr ng-repeat="x in ctrl.SprintViewModel.Tasks">
<td style="width: 40%">{{ x.Description }}</td>
<td>{{ x.TimeEst }}</td>
<td>{{ x.TimeUsed }}</td>
<td>{{ x.TimeRemaining }}</td>
<td>{{ ctrl.CalcPrecisionOfEstimat(x.TimeUsed,x.TimeRemaining,x.TimeEst) | number:2}} %</td>
<td>{{x.Price}}</td>
</tr>
<tr>
<td>Ialt</td>
<td>{{ ctrl.TotalEstimat() }}</td>
<td>{{ ctrl.TotalTimeUsed() }}</td>
<td>{{ctrl.TotalTimeRemaining()}}</td>
<td>{{ctrl.TotalPrecision()}}</td>
<td>{{ctrl.TotalPrice()}}</td>
</tr>
</table>
* Forbrug + Udestående i forhold til estimat
<br/>
Udestående opgaver er planlagt ind i næstkommende sprint.
</div>
</div>
</form>
<script>
var app = angular.module('myApp', []);
app.controller('taskCtrl', function($scope, $http) {
var ctrl = this;
ctrl.SprintViewModel = null;
ctrl.TotalEstimat=function() {
var totalEstimat=0;
for (i=0; i<ctrl.SprintViewModel.Tasks.count;i++) {
totalEstimat += ctrl.SprintViewModel.Tasks[i].Estimate;
}
return totalEstimat;
}
ctrl.TotalPrecision = function () {
var totalPrecision=0;
angular.forEach(ctrl.SprintViewModel.Tasks, function (value, key) {
totalPrecision += Number(value);
});
$http.post('SprintRapport.aspx/GetSprintViewModel', {})
.then(function(response, status, headers, config) {
console.log("I success");
ctrl.SprintViewModel = response.data.d;
});
});`
As already mentioned I get a nullreference every when the page-load on all the methods in the last row, because ctrl.SprintviewModel is undefined. I have only included one of the methods for simplicity, the problem is the same for all of them.
So my question is how do I make sure that ctrl.TotalEstimat() first get called then ctrl.SprintViewModel is assigned?
You can add ng-if condition to the last <tr> which resolves to true when data is ready to populate in your controller. So you can define $scope.loading = false initially and once your code is ready to populate you set $scope.loading=true and that will call $digest cycle internally and your view gets updated.
There are several things that you could do. I've fixed this kind of issue by placing guard conditions in the functions. These check that the necessary variables have been set before continuing. So adding if (!ctrl.SprintViewModel) return; at the beginning of the function as follows:
ctrl.TotalEstimat=function() {
// Guard Condition to prevent function executing in invalid state.
if (!ctrl.SprintViewModel) return;
var totalEstimat=0;
for (i=0; i<ctrl.SprintViewModel.Tasks.count;i++) {
totalEstimat += ctrl.SprintViewModel.Tasks[i].Estimate;
}
return totalEstimat;
}
It's another option, but as you have already alluded to, I think that promises and the $q library is the proper angular way to fix this sort of thing.

Deleting firebase data that is displayed as a Table using AngularJS

I have an HTML file which displays a data set in firebase as a table. As the data is in firebase I am using (key,client) in clientInfo to get the key as well. This key is used for the purpose of deleting a row.
<table class="meeting">
<tr ng-repeat="(key, client) in clientInfo">
<td>{{ client.name }}</td>
<td>{{ client.discount }}</td>
<td>{{ client.vms }}</td>
<td><button class="btn btn-delete tooltip" ng-click="deleteClient(key)">
<span>Delete this client</span></button>
</td>
</tr>
</table>
But while clicking the button, the deleteClient(key) function is not called. Is it because the information of key isnt available on a Cell(td) level and is available only on a Row(tr) level in the HTML?
I am using an older version of Firebase where $as.Object() is required to store data as an Object.
My controller in Angular
myApp.controller('DataController', function ($scope, $rootScope, $firebase, FIREBASE_URL) {
var ref = new Firebase(FIREBASE_URL + '/clientInfo');
var clientInfo = $firebase(ref);
$scope.clientInfo = clientInfo.$asObject();
$scope.deleteClient = function(key) {
clientInfo.$remove(key);
};
Is it possible to do this in Table format? If so, what am I doing wrong? Please help me solve this issue

AngularJS: page displays NaNs and displays data when it gets from server. How to prevent it?

Consider this conroller
$scope.transaction = {};
$scope.transactions = Transaction.query();
$scope.save = function() {
var transaction = new Transaction();
transaction.name = $scope.transaction['name'];
transaction.debit = $scope.transaction['debit'];
transaction.date = $scope.transaction['date'];
transaction.amount = $scope.transaction['amount'];
transaction.category = $scope.transaction['category'].uuid;
//noinspection JSUnresolvedFunction
transaction.$save();
$scope.transactions.push(transaction);
console.log('transaction saved successfully', transaction);
};
and this HTML
<tbody ng-repeat="transaction in transactions | orderBy: transaction.created_on">
<td>{{ transaction.name }}</td>
<td>{{ transaction.amount | currency }}</td>
<!-- custom filter to display type of transaction -->
<td>{{ transaction.debit | transactionType }}</td>
<!-- added dateInMillis to pass to date to filter Angular way -->
<td>{{ transaction.created_on | dateInMillis | date: 'medium'}}</td>
<td>{{ transaction.category.name }}</td>
<td>
</tbody>
Problem
When I add transaction, it immediately displays bunch of NaNs and then once the server comes back with saved data, it replaces those NaNs with actual data
How can I prevent that from happening? Its not a good UX
Without seeing all the code related to the Transaction object its hard to know for sure what the problem could be. At a glance I think you need a callback function attached to transaction.$save() method.
transaction.$save(function(u, putResponseHeaders) {
// This is $save's success callback
$scope.transactions.push(transaction);
console.log('transaction saved successfully', transaction);
});

Categories