Monday, January 28, 2019

javascript - AngularJS : Initialize service with asynchronous data



I have an AngularJS service that I want to initialize with some asynchronous data. Something like this:



myModule.service('MyService', function($http) {
var myData = null;


$http.get('data.json').success(function (data) {
myData = data;
});

return {
setData: function (data) {
myData = data;
},
doStuff: function () {

return myData.getSomeData();
}
};
});


Obviously this won't work because if something tries to call doStuff() before myData gets back I will get a null pointer exception. As far as I can tell from reading some of the other questions asked here and here I have a few options, but none of them seem very clean (perhaps I am missing something):



Setup Service with "run"




When setting up my app do this:



myApp.run(function ($http, MyService) {
$http.get('data.json').success(function (data) {
MyService.setData(data);
});
});


Then my service would look like this:




myModule.service('MyService', function() {
var myData = null;
return {
setData: function (data) {
myData = data;
},
doStuff: function () {
return myData.getSomeData();
}

};
});


This works some of the time but if the asynchronous data happens to take longer than it takes for everything to get initialized I get a null pointer exception when I call doStuff()



Use promise objects



This would probably work. The only downside it everywhere I call MyService I will have to know that doStuff() returns a promise and all the code will have to us then to interact with the promise. I would rather just wait until myData is back before loading the my application.




Manual Bootstrap



angular.element(document).ready(function() {
$.getJSON("data.json", function (data) {
// can't initialize the data here because the service doesn't exist yet
angular.bootstrap(document);
// too late to initialize here because something may have already
// tried to call doStuff() and would have got a null pointer exception
});
});



Global Javascript Var
I could send my JSON directly to a global Javascript variable:



HTML:







data.js:



var dataForMyService = { 
// myData here
};


Then it would be available when initializing MyService:



myModule.service('MyService', function() {

var myData = dataForMyService;
return {
doStuff: function () {
return myData.getSomeData();
}
};
});


This would work too, but then I have a global javascript variable which smells bad.




Are these my only options? Are one of these options better than the others? I know this is a pretty long question, but I wanted to show that I have tried to explore all my options. Any guidance would greatly be appreciated.


Answer



Have you had a look at $routeProvider.when('/path',{ resolve:{...}? It can make the promise approach a bit cleaner:



Expose a promise in your service:



app.service('MyService', function($http) {
var myData = null;


var promise = $http.get('data.json').success(function (data) {
myData = data;
});

return {
promise:promise,
setData: function (data) {
myData = data;
},

doStuff: function () {
return myData;//.getSomeData();
}
};
});


Add resolve to your route config:



app.config(function($routeProvider){

$routeProvider
.when('/',{controller:'MainCtrl',
template:'
From MyService:
{{data | json}}
',
resolve:{
'MyServiceData':function(MyService){
// MyServiceData will also be injectable in your controller, if you don't want this you could create a new promise with the $q service
return MyService.promise;
}
}})
}):



Your controller won't get instantiated before all dependencies are resolved:



app.controller('MainCtrl', function($scope,MyService) {
console.log('Promise is now resolved: '+MyService.doStuff().data)
$scope.data = MyService.doStuff();
});



I've made an example at plnkr: http://plnkr.co/edit/GKg21XH0RwCMEQGUdZKH?p=preview


No comments:

Post a Comment

plot explanation - Why did Peaches' mom hang on the tree? - Movies & TV

In the middle of the movie Ice Age: Continental Drift Peaches' mom asked Peaches to go to sleep. Then, she hung on the tree. This parti...