Waiting for an API before starting an AngularJS app
Background
I have an Angular app that uses data from an API endpoint for various things. Let’s say that the API endpoint is /user-info
, and it returns data that looks like this:
This data is used by different services and directives in my app.
The problem
Ordinarily, I’d retrieve the data with something like this:
This is all well and good when the data is only required in one place, but what if myServiceOne
and myDirectiveTwo
both need data from this endpoint, and they’re used on the same page? If you followed the above $http.get
logic in both places, you’ll end up sending the same request twice, doubling the network load.
A possible solution?
I thought I could easily solve this by making a simple cache. To do this, I refactored my two calls to $http.get
into a service that checks the cache before making an HTTP request.
For brevity, I’ve omitted error handling. In real code I handle errors, promise.
This solution looks pretty good, but when I used it, it didn’t solve the issue. I was still seeing multiple requests being made to /user-info
and I couldn’t figure out why. Then I realised that it’s a race condition of sorts. When the app loads, UserInfo.get` is run twice in quick succession (once by the directive that uses it, once by the service), which means that the API hasn’t had a chance to respond yet, and the data hasn’t been cached.
The actual solution
I decided that the best way for me to tackle this issue was for the data to be available when the app starts. As far as I can tell, Angular doesn’t have a way to resolve a promise before starting an app. But with some creativity you can get around this.
The beginning of my index.html
looked like this:
I removed the ng-app
attribute to stop Angular automatically starting the app on load. Then, in my app.js
file (the one that has the root of my Angular module, I added a self executing function):
As the above code shows, the function retrieves Angular’s $http
module, uses it to get the required info, makes it into a module, then manually starts the app, passing in the module.
Now, when the app starts, the cached response from /user-info
can easily be injected. For example, here’s how a controller might use it:
What other ways have you found to solve this problem? Let us know on Twitter.