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.