Dive Into ng-packagr on this Episode of Angular Air_870x220

The Grid has a tough job: it has to support many features while tracking thousands of data items. To keep performance acceptable, we employ the usual tricks for Angular components:

  • Set the Change Detection strategy to OnPush for components that respond only to changes in their input properties
  • Execute event handlers outside of the Angular Zone so we don't trigger change detection cycles
  • Debounce events in order to avoid redundant processing

With so much preparation in place the performance should be good, right? Well, it seemed to be. Until the reports started piling up; the Grid was hardly usable with IE 11 in surprisingly-common scenarios. In case you weren't aware, IE will cough at the slightest sign of trouble. It's the proverbial canary in a coal mine.

To diagnose the problem, we set up a demo on StackBlitz and started profiling:

Performance Profile - bad

We were generous with 100K data items and 33 columns.

As you can see, we achieved the glacial top speed of 4fps during scrolling in Chrome. The number of DOM elements kept around was staggering; 255K at the highest point.

You can get a feel for the actual scroll performance in the demo below:

For the most part, we trust Angular to do the right thing when it comes to updating the DOM. Well, as it turns out, it was doing the wrong thing for tables. On each update, ngFor would cycle through each table row and apply the changes to it; removing, replacing, or moving rows. This turned out to be a performance disaster as IE would recalculate the table layout on each change.

A simplified version of what the Grid does could be demonstrated in this snippet. Notice how each page replaces all 10 rows (20 updates in total).

Lucky for us, the NgFor directive has an escape hatch: the trackBy function. Instead of creating a new row for each data item, we can track it by index. The effect is that table rows would only be added or removed when the number of items per page changes. As the Grid page size is uniform, this will happen rarely. Most of the time, the row elements would be reused while their content is updated.

Let's look at the updated snippet:

Notice that no rows are added or removed on page changes. This is due to the trackBy function that returns the item index:

<table>
  <tr *ngFor="let item of data; trackBy: trackIndex">
    <td>{{ item.value }}</td>
  </tr>
</table>
public trackIndex(index: number): any {
  return index;
}

With this small modification, the performance profile for the Grid is dramatically improved:

Performance Profile - Good

We're hitting 60fps without a sweat and the DOM element count remains constant. Try it out:

Takeaway

By reusing DOM elements in the Grid table, we were able to improve performance with paging and virtual scrolling. Initial rendering time is not affected.

We recommend upgrading @progress/kendo-angular-grid to v3.7.0 or later, especially if you're targeting IE 11. If you're new to Kendo UI for Angular, click the link below to get started with a free trial of the latest version.

Get Started with Kendo UI for Angular

Further Reading

  • This is not the first time we've faced such issue. Our colleague, Georgi Krustev, has an excellent write-up on NgFor and performance in Blazing Fast List Rendering in Angular.
  • The Faster Angular Applications series by Minko Gechev are a great reading that will make you rethink how you approach performance in Angular applications.

Happy coding!


Tsvetomir Tsonev
About the Author

Tsvetomir Tsonev

Tsvetomir is a developer on the Kendo UI team. He enjoys helping others as much as creating software. In his spare time, Tsetso loves reading science fiction and photographing nature.

Related Posts

Comments

Comments are disabled in preview mode.