I recently posted a detailed description of the issue I am facing here at SO. As I couldn\'t send an actual $http
request, I used timeout to simulate asynchrono
tosh shimayama have a solution but you can simplify a lot if you use the fact that $http returns promises and that promises can return a value:
app.factory('myService', function($http, $q) {
myService.async = function() {
return $http.get('test.json')
.then(function (response) {
var data = reponse.data;
console.log(data);
return data;
});
};
return myService;
});
app.controller('MainCtrl', function( myService,$scope) {
$scope.asyncData = myService.async();
$scope.$watch('asyncData', function(asyncData) {
if(angular.isDefined(asyncData)) {
// Do something with the returned data, angular handle promises fine, you don't have to reassign the value to the scope if you just want to use it with angular directives
}
});
});
A little demonstration in coffeescript: http://plunker.no.de/edit/ksnErx?live=preview
Your plunker updated with my method: http://plnkr.co/edit/mwSZGK?p=preview
Related to this I went through a similar problem, but not with get or post made by Angular but with an extension made by a 3rd party (in my case Chrome Extension).
The problem that I faced is that the Chrome Extension won't return then()
so I was unable to do it the way in the solution above but the result is still Asynchronous.
So my solution is to create a service and to proceed to a callback
app.service('cookieInfoService', function() {
this.getInfo = function(callback) {
var model = {};
chrome.cookies.get({url:serverUrl, name:'userId'}, function (response) {
model.response= response;
callback(model);
});
};
});
Then in my controller
app.controller("MyCtrl", function ($scope, cookieInfoService) {
cookieInfoService.getInfo(function (info) {
console.log(info);
});
});
Hope this can help others getting the same issue.
I had the same problem, but when I was surfing on the internet I understood that $http return back by default a promise, then I could use it with "then" after return the "data". look at the code:
app.service('myService', function($http) {
this.getData = function(){
var myResponseData = $http.get('test.json').then(function (response) {
console.log(response);.
return response.data;
});
return myResponseData;
}
});
app.controller('MainCtrl', function( myService, $scope) {
// Call the getData and set the response "data" in your scope.
myService.getData.then(function(myReponseData) {
$scope.data = myReponseData;
});
});
Please try the below Code
You can split the controller (PageCtrl) and service (dataService)
'use strict';
(function () {
angular.module('myApp')
.controller('pageContl', ['$scope', 'dataService', PageContl])
.service('dataService', ['$q', '$http', DataService]);
function DataService($q, $http){
this.$q = $q;
this.$http = $http;
//... blob blob
}
DataService.prototype = {
getSearchData: function () {
var deferred = this.$q.defer(); //initiating promise
this.$http({
method: 'POST',//GET
url: 'test.json',
headers: { 'Content-Type': 'application/json' }
}).then(function(result) {
deferred.resolve(result.data);
},function (error) {
deferred.reject(error);
});
return deferred.promise;
},
getABCDATA: function () {
}
};
function PageContl($scope, dataService) {
this.$scope = $scope;
this.dataService = dataService; //injecting service Dependency in ctrl
this.pageData = {}; //or [];
}
PageContl.prototype = {
searchData: function () {
var self = this; //we can't access 'this' of parent fn from callback or inner function, that's why assigning in temp variable
this.dataService.getSearchData().then(function (data) {
self.searchData = data;
});
}
}
}());
A much better way I think would be something like this:
Service:
app.service('FruitsManager',function($q){
function getAllFruits(){
var deferred = $q.defer();
...
// somewhere here use: deferred.resolve(awesomeFruits);
...
return deferred.promise;
}
return{
getAllFruits:getAllFruits
}
});
And in the controller you can simply use:
$scope.fruits = FruitsManager.getAllFruits();
Angular will automatically put the resolved awesomeFruits
into the $scope.fruits
.
I really don't like the fact that, because of the "promise" way of doing things, the consumer of the service that uses $http has to "know" about how to unpack the response.
I just want to call something and get the data out, similar to the old $scope.items = Data.getData();
way, which is now deprecated.
I tried for a while and didn't come up with a perfect solution, but here's my best shot (Plunker). It may be useful to someone.
app.factory('myService', function($http) {
var _data; // cache data rather than promise
var myService = {};
myService.getData = function(obj) {
if(!_data) {
$http.get('test.json').then(function(result){
_data = result.data;
console.log(_data); // prove that it executes once
angular.extend(obj, _data);
});
} else {
angular.extend(obj, _data);
}
};
return myService;
});
Then controller:
app.controller('MainCtrl', function( myService,$scope) {
$scope.clearData = function() {
$scope.data = Object.create(null);
};
$scope.getData = function() {
$scope.clearData(); // also important: need to prepare input to getData as an object
myService.getData($scope.data); // **important bit** pass in object you want to augment
};
});
Flaws I can already spot are
getData
can only accept the obj
parameter in the form of an object (although it could also accept an array), which won't be a problem for many applications, but it's a sore limitation$scope.data
with = {}
to make it an object (essentially what $scope.clearData()
does above), or = []
for an array, or it won't work (we're already having to assume something about what data is coming). I tried to do this preparation step IN getData
, but no luck.Nevertheless, it provides a pattern which removes controller "promise unwrap" boilerplate, and might be useful in cases when you want to use certain data obtained from $http in more than one place while keeping it DRY.