Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Show loading animation for slow script using AngularJS?

In angularjs 1.2 operations like filtering an ng-repeat with many rows (>2,000 rows) can become quite slow (>1 sec).
I know I can optimize execution times using limitTo, pagination, custom filters, etc. but I'm still interested to know if it's possible to show a loading animation while the browser is busy running long scripts.

In case of angular I think that could be invoked whenever $digest is running because that seems to be the main function that takes up most time and might be called several times.

In a related question there were no useful answers given. Any help greatly appreciated!

like image 542
Horen Avatar asked Apr 11 '14 03:04

Horen


1 Answers

The problem is that as long as Javascript is executing, the UI gets no chance to update. Even if you present a spinner before filtering, it will appear "frozen" as long as Angular is busy.

A way to overcome this is to filter in chunks and, if more data are available, filter again after a small $timeout. The timeout gives the UI thread a chance to run and display changes and animations.

A fiddle demonstrating the principle is here.

It does not use Angular's filters (they are synchronous). Instead it filters the data array with the following function:

function filter() {     var i=0, filtered = [];     innerFilter();      function innerFilter() {         var counter;         for( counter=0; i < $scope.data.length && counter < 5; counter++, i++ ) {             /////////////////////////////////////////////////////////             // REAL FILTER LOGIC; BETTER SPLIT TO ANOTHER FUNCTION //             if( $scope.data[i].indexOf($scope.filter) >= 0 ) {                 filtered.push($scope.data[i]);             }             /////////////////////////////////////////////////////////         }         if( i === $scope.data.length ) {             $scope.filteredData = filtered;             $scope.filtering = false;         }         else {             $timeout(innerFilter, 10);         }     } } 

It requires 2 support variables: $scope.filtering is true when the filter is active, giving us the chance to display the spinner and disable the input; $scope.filteredData receives the result of the filter.

There are 3 hidden parameters:

  • the chunk size (counter < 5) is small on purpose to demonstrate the effect
  • the timeout delay ($timeout(innerFilter, 10)) should be small, but enough to give the UI thread some time to be responsive
  • the actual filter logic, which should probably be a callback in real life scenarios.

This is only a proof of concept; I would suggest refactoring it (to a directive probably) for real use.

like image 125
Nikos Paraskevopoulos Avatar answered Oct 12 '22 00:10

Nikos Paraskevopoulos