Sometimes we need to initialize an Angular filter asynchronously. For example, information for filtering might come from a server. Here is how one can do this.
Here is the page setup
1 | <body ng-app="filterExample"> |
initial attempt - does not work
My initial attempt was the most straightforward approach: just return a promise
from a module.filter
callback. After 1 second I would resolve the promise with the actual
function.
1 | angular.module('filterExample') |
Unfortunately, this does not work. Angular tries to parse the expression 'a string' | aFilter
and execute aFilter
as a function. But this is not a function, instead it is a promise object!
filter substitution
A different approach is to use an inner worker function inside a filter. This worker function could be replaced at any moment, for example after a timeout of 1 second.
1 | angular.module('filterExample', []) |
You can see the filter in action at http://jsbin.com/zucunu/2
Output:
a string filtered initially
// after 1 second changes to
a string filtered with delayed!!!
Main points in the script:
- We can inject other services into filter registration function
registerAFilter
. In this case I injected$timeout
service. - The returned function
tempFilter
is the one called by Angular digest cycle over an over. It has access to the actual worker functionfilterFn
via closure. filterFn
points atinitialFilter
at first, then points atnewFilter
.$timeout
service is integrated with digest cycle, thus we did not have to call$rootScope.$apply
after changing the filter inner function.- It would make sense to cloak the page until the filters have been resolved to avoid sudden text changes.
conclusion
This is a solution to delayed filter initialization using available AngularJs services. Instead of implementing promise logic deep in the Angular expression parsing, the framework allows us to change our code inside the filter function.
We only want to initialize the filter once, and then use the result multiple times. Filtering potentially runs multiple times and any async processing per item would lead to bad performance and page flickering.