Reputation: 2574
I want to create an angular service that grabs data at "start-up" and builds up an array that will not change for the lifetime of the application. I don't want to return the promise of the http request. Caching the result would still incurs the cost of the 'someExpensiveFunctionCall'. What's the best way to handle this situation that can guarantee that the service will have the result populated and can be used in a controller and/or another service.
angular.module('app')
.service('MyService', function($http, defaults) {
var result = [];
$http.get('/some/url').then(function(data) {
if(angular.isDefined(data)) {
_.forEach(data, function(item) {
result.push(someExpensiveFunctionCall(item.value));
});
} else {
result = angular.copy(defaults.defaultResult);
}
});
return {
result: result
};
})
.controller(function(MyService) {
var someData = MyService.result;
})
.service(function(MyService) {
var someData = MyService.result;
});
Upvotes: 1
Views: 113
Reputation: 5753
I don't want to return the promise of the http request.
It seems like you want to do something that is asynchronous; synchronously. This can't work (reliably) without some sort of callback, promise, sync XHR, or other mechanism.
Knowing that we can evaluate some options. (in order of most practical)
Use your routers resolve
property. This will complete some promise returning operation before instantiating your controller. It also (in ui-router
and ngRoute
) means the result of the promise will be injected into the controller under the specified key name.
Use the promise.
(aka #1 manually)
.factory('MyService',function($http,defaults){
var ret = {result: []};
ret.promise = $http.get('url').then(function(data){
if(angular.isDefined(data)){
_.forEach(data,function(item){
ret.result.push(someExpensiveFunctionCall(item.value));
});
}else{
_.forEach(angular.copy(defaults.defaultResult),function(val){
ret.result.push(val);
});
}
return ret;
});
return ret;
})
.controller('MyCtrl',function(MyService){
MyService.promise.then(myCtrl);
function myCtrl(){
var result = MyService.result
}
})
result
inside the body of a controller or service. But instead, use the view bindings, filters and interactions to call scope functions that act on the data. Since the resolution of a promise will invoke a digest cycle. Your view will render when the data arrives.There was a bug in your code if the data
happens to be undefined. You are de-referencing result
by setting it to a copy of the defaults.
So all references to MyService.result
will always be an empty array and never the default value.
In order to maintain the reference I'd loop over the defaults and add them to result
with result.push
.
Hope this helps!
Upvotes: 1