aman
aman

Reputation: 6252

Slow rendering of HTML table when binding JSON data

Using angularjs (1.3) with webapi here.

I have UI where the user can upload a excel file. My api reads the excel file and returns rows data back to the UI in JSON.

The UI then reads the JSON and binds it back the UI table.

The rows and columns of this UI table are dynamically generated and are not fixed, because of which I am using contenteditable in HTML as the user can add more rows.

I can read from the the JSON fine and populate the array that holds these json values. The issue is while rendering, the screen is frozen and takes time to render all the data. I am currently binding about 800 rows and the screen freezes and takes about 10-15 seconds or more to fill up the UI table. I would be having lot more data so looking for a solution for this.

I tried to debug and can see that there is no issue getting data back from the API, and reading JSON from the API. There is also no issue while populating the array. Once the array populates thats when the issue comes. The UI freezes and takes time to render this data.

I am not sure whats going on here or why it takes so time to render. Below is some sample relevant code:

//Read json from the API

$http.get('https://api.myjson.com/bins/d1ugw').success(function(data) {
if (data.length > 0) {   

  $scope.setJson = data;
  $scope.initializeTable(true);
  var columns = $scope.targetTable.columns;

  //These are the 3 columns of the Table but can vary dynamically(currently just hardcoding it)
  var refColName = "state, month , year";

  //Push the columns to the table array
  var colArray = refColName.split(',');
  for (var i = 0; i < colArray.length; i++) {
    $scope.targetTable.columns.push({
      id: columns.length,
      refColName: refColName.split(',')[i]
    });
  }

  //Read the values from the json
  var idValues = $scope.getTableValues($scope.setJson, 'id');
  var commentValues = $scope.getTableValues($scope.setJson, 'comment');
  var rowValues = $scope.getTableValues($scope.setJson, 'refcol');
  var setIdValues = $scope.getTableValues($scope.setJson, 'sid');

  //Push the data back to the table array.
  $scope.pushRowData($scope.targetTable, rowValues, commentValues, idValues, setIdValues);
  //Till the above steps everything happens quickly and I can see $scope.targetTable being populated with my json. 
  //But after the above step the screen just freezes and takes time to show the entire data on the UI table.
 }
  });

Below is the relevant code for the UI:

<tbody>
    <tr ng-repeat="r in targetTable.rows">
      <td class="fixed-width">
        <span>
            <a class="btn-xs" ng-show="row == $index" ng-if="targetTable.rows.length > 1"><i class="fa fa-times-circle" aria-hidden="true"></i></a>
        </span>
        <span contenteditable="true" ng-model="r.tableId" ng-change="addNewRow(r.tableId, r)">{{r.tableId}}</span>
      </td>
      <td class="fixed-width" contenteditable="true" ng-repeat="column in targetTable.columns" ng-model="r[column.id]" ng-change="rowDataChange(r[column.id])"></td>
      <td class="comment-fixed-width" contenteditable="true" ng-model="r.comment" ng-change="rowDataChange(r.comment)"></td>
      <td class="blank fixed-width" colspan="2" ng-model="r[column.id]"></td>
    </tr>
  </tbody>

I have created the below JSFiddle to show my example and issue I am facing.

http://jsfiddle.net/aman1981/u1vbeos5/312/

I have also added comments in my jsfiddle for showing what method does what.

Would appreciate if anyone can help my resovling this issue.

Upvotes: 0

Views: 1930

Answers (3)

Christian Meyer
Christian Meyer

Reputation: 1781

Here are some performance stats:

with contenteditable (~4000 digest calls) = 1.800ms -> http://prntscr.com/lweugn

without contenteditable (~4 digest calls) = 1.300ms -> http://prntscr.com/lweusn

with pagination just showing the first 50 results = 0.200ms -> http://prntscr.com/lwev09

You loose the most performance because of the DOM changes obviously. But keep in mind that the number and duration of digest cycles is key for good performance. Especially when you have a huge amount of watchers. Here is a Direct comparison: http://prntscr.com/lwf1nn As you can see the digest loop is burning 30% of your performance overall but is not the reason for your frame drop. The frame drop is mostly caused of the DOM changes. Drawing such a big table takes some time.

Further the table starts rendering when your REST call is finished. This call takes in my case roughly additional 1.700ms. So it takes nearly 3.500ms from start until rendered results. Even with pagination 1.900ms.

I would recommend a pagination with search but you can try to increase the performance anyway.

Helpful links would be:

https://stackoverflow.com/a/47347260/8196542

https://www.codeproject.com/Articles/1173869/%2FArticles%2F1173869%2Fng-repeat-performance-degradation-at-case-of-very

Upvotes: 1

Idan Marko
Idan Marko

Reputation: 94

First, I recommend that you upgrade the Angular version to the most advanced of course if possible. Also check the animate version. Beyond that you thought of using an advanced tabular component such as ag-grid ?, I can load 10000 rows without any problem with it. https://www.ag-grid.com/

Upvotes: 1

Christian Meyer
Christian Meyer

Reputation: 1781

Your code is triggering the $digest loop over and over. The "watch" method counts how often the $digest cycle is actually called:

var nbDigest = 0;

$scope.$watch(function() {
  nbDigest++;
  console.log(nbDigest);
});

I bet this is the cause of your performance issues.

Upvotes: 0

Related Questions