I want to make a function which fetches some info from an ajax URL. E.g in my service, I would have the following method:
this.getFavColor = function(id)
{
return $http.get('/colors/get/' + id);
}
And in my controller, I would do the following:
$scope.favColor = UserService.getFavColor( id );
The problem however is, $scope.favColor will be assigned a promise in this case, and the only way to actually change it to the value returned by ajax, is to set a .success() callback on the promise and use it to update the value.
However, this is quickly becoming cumbersome if I have a lot of things that have to be fetched via ajax. Is there any shortcut, such as may be doing this?
this.getFavColor = function(id, variableToChange)
{
return $http.get('/colors/get/' + id).success(function(jsonResult)
{
variableToChange = jsonResult.favColor;
});
}
And then doing the following in the controller:
UserService.getFavColor( id, $scope.favColor );
Will this method actually work?
Note: I've already considered $resource but I cannot set up a REST api for my ajax, so please don't suggest it.
The way $resource does this is by returning an empty object immediatly and then adding data to this object once the response arrives from the server. That's why $resource can only return objects or arrays, not primitives.
ng-bind (and the shorthand {{ }}) actually resolves promises though, so this might be a better solution. I've created a plnkr with three different examples: http://plnkr.co/edit/WOrU5eMOmsgK4wuiSCHu?p=preview
// data.json: {"color":"Blue"}
app.service('UserService',function($http, $q){
return {
// return value can be accessed by {{value.data.color}}
getFavColor: function(id){
return $http.get('data.json');
},
// return value can be accessed by {{value}}
getFavColorWithQ: function(id){
var def = $q.defer();
$http.get('data.json').success(function(data){
def.resolve(data.color);
});
return def.promise;
}
// return value can be accessed by {{value.color}}
,resourceExample: function(id){
var response = {};
$http.get('data.json').success(function(data){
response.color = data.color;
});
return response;
}
}
});
Related
Hi I have created a service which gets a json from a remote server, not I need to get the result and set it as a property of the service, but I don't know how to get the property out of the function
app.service('varService',['$http', function($http){
var ip = myip;
window.city = '';
$http.get('http://ip-api.com/json/'+ip)
.then(function(data) { window.city = data.data.city; });
this.city=city;
}]);
the property this.city doesn't receive any value , however when I do console.log inside .then() the value exists, how do I solve the problem, how do I get value out of $http.then()?
Because $http.get returns a promise, when you assign the city value, the request hasn't actually returned from the web server yet.
You need to return the promise from your service and instead handle your .then() callback handler in your invoking controller.
app.service('varService',['$http', function($http){
var ip = myip;
this.getIp = $http.get('http://ip-api.com/json/'+ip);
}]);
// controller
varService.getIp.then(function(data) { window.city = data.data; });
This is the recommended approach by the John Papa AngularJS Styleguide and it allows you to chain multiple promises together.
There you can't get data return by ajax as soon as you execute that line of code, you should use .then callback to retrieve data over there. You could use .then over $http call to get data from ajax.
app.service('varService',['$http', function($http){
var ip = myip;
var self = this;
self.getCity = function (){
return $http.get('http://ip-api.com/json/'+ip)
.then(function(data) {
self.city = data.data.city;
return self.city; //do return city retrieved
});
}
}]);
I am trying to wrap the getstream API in an angular service (factory)
Here is my code:
.factory('FeedStream', function($http, $q) {
var client = stream.connect('xxxxxxxxxxx');
return {
feed : function() {
$http.get('/api/feed/auth/')
.success(function(auth) {
var user_feed = client.feed('user', auth.user, auth.token);
console.log(user_feed.get());
user_feed.get().then(function(data) {
console.log(data);
return data;
})
})
},
}
First I get the user_id and auth token from my server using the endpoint /api/feed/auth/. This data is returned in an angular promise.
Next, I use this data to call the getstream api to get the user_feed object. If I return this object (user_feed) it is undefined in the controller. If I print it to the console here in the service, it has the correct value. I've noticed that the print happens half a second or so after the return. Why is the assignment of this variable happening asynchronously?
Now if I call the get method on this user_feed object inside a console.log statement, a Javascript promise object is printed out. If I return user_feed.get() it returns undefined to the controller. If I call it here in the service like in my code above, and return the data object in the promise then statement, it returns undefined to the controller. However if I print the data object, it has the correct value.
Why can't I return any objects from this service? Am I missing some fundamental aspect of using promises?
You haven't returned feed promise object as well as data haven't been returned from feed method correctly. So for achieving the same thing do use .then over $http to maintain promise chaining
Code
return {
feed : function() {
//return `$http.get` promise
return $http.get('/api/feed/auth/')
.then(function(response) {
var auth = response.data;
var user_feed = client.feed('user', auth.user, auth.token);
console.log(user_feed.get());
//return `$http.get` to return internal promise
return user_feed.get().then(function(data) {
console.log(data);
return data;
})
})
},
}
Controller
FeedStream.feed().then(function(data){
console.log("data returned by user feed", data)
})
I am using angular resource modul to work with my restfull webserver. For exampe i have a method which returns an array of 10 elements and what i want to do is just save result into some javascript variable (not in angular scope) called books.
So i have written a simple method which looks like this:
function getBooks(user_id) {
var books = [];
BookFactory.getBooks.query({ id: user_id }).$promise.then(function (result) {
angular.forEach(result, function(i) {
books.push(i);
});
});
return books;
}
Let's assume that BookFactory.getBooks.query works as expected and truly returns 10 elements.
So there is a simple logic inside this function, i just push each element to the array books.
Also i have a test function for testing getBooks() function. Here it is:
$scope.testGetBooksMethod = function (user_id) {
var resut = getBooks(user_id);
alert(resut.length);
};
The result in alert will be always 0. I know that this part of code:
BookFactory.getBooks.query({ id: user_id }).$promise.then(function (result) {
angular.forEach(result, function(i) {
books.push(i);
});
});
works asynchronously and until server request is processing, function getBooks() returns an empty array of books (please correct me if i am wrong).
Here is a question, how can i edit my function, to work it correctly. I want to get data from rest, fill the array books with this data and then return it.
Thanks in advance.
I want to get data from rest, fill the array books with this data and then return it.
This is the thing. Asynchronous programming is not really compatible with synchronous. So you can't just combine them. However you could work in a synchronous-like way, and this is where promises comes very powerful. You don't return data, you return stateful Promise object instead.
So instead of returning books return a Promise for this books:
function getBooks(user_id) {
return BookFactory.getBooks.query({ id: user_id }).$promise;
}
Then in consuming code you would use this wrapper object with underlying state control mechanism which will proceed when books are loaded and available:
$scope.testGetBooksMethod = function (user_id) {
getBooks(user_id).then(function(books) {
alert(books.length);
});
};
You need to use promise concept here, testGetBooksMethod of controller function will wait till getBooks of service method will complete its call. For that you need to return the BookFactory.getBooks.query promise from getBooks function. After data gets retrieve the books are will get created & will return from the getBooks method. While calling getBooks method you need to use .then function that will continue the chain promise thing, when data is returned from the getBooks this then function will get the data returned from the your service.
function getBooks(user_id) {
var books = [];
return BookFactory.getBooks.query({ id: user_id }).$promise.then(function (result) {
angular.forEach(result, function(i) {
books.push(i);
});
return books;
});
}
Controller
$scope.testGetBooksMethod = function (user_id) {
getBooks(user_id).then(function(resut){
alert(resut.length);
});
};
I'm trying to store some user data in a service that will be accessible and modified across different controllers, and the data will be initially pulled via a $http call. Since the data has to be loaded, I've used promises in my previous code, but this can be quite irritating to write. For instance, even a basic getter function has to be written as the following
UserData.getData().then(function(data) {
//do something with the data
})
where UserData.getData() always returns a promise via deferred.promise (if the data has already been pulled, then resolve immediately). I'm wondering if there is anyway to split a) $http calls, and b) getter and setter methods into two different services so that if I call getter and setter methods from b), I don't need to wrap everything with then?
For instance, I'm trying to make UserFactory in charge of the $http call, and UserData in charge of getters and setters. However, I can't get the code to work since UserData.getData() will return undefined, and wondering if anyone can help? (I don't really want to have to use then everywhere).
angular.module('testApp', [])
//mocks a service that gets data from a server
.factory('UserFactory', function($timeout, $q) {
return {
getData: function() {
var deferred = $q.defer();
$timeout(function() {
deferred.resolve({title: 'hello world'});
}, 1000);
return deferred.promise;
}
}
})
.factory('UserData', function(UserFactory) {
var data;
return {
//if already pulled, use existing data
getData: function() {
if (data) return data;
UserFactory.getData().then(function(res) {
data = res;
return data;
})
}
}
})
http://jsfiddle.net/QNLk2/1/
The then method is executed asynchronously so the return method as no effect there. That is why until the data has arrived every call to getData will return undefined.
You can try the following approach, where you can specify a callback if you want to be notified when data is ready, or simply wait for the data to be populated in the return variable.
.factory('UserData', function(UserFactory) {
var data = {};
return {
getData: function(callback) {
UserFactory.getData().then(function(res) {
angular.extend(data, res);
if (callback) {
callback(data);
}
});
return data;
}
}
})
I want to be able to return pre-loaded data from an angular service if it's already there and if it's not I want to get the data from a web service.
Obviously one of these operations is synchronous and the other isn't so I want to wrap the simple return in an asynchronous way so that I don't have to have lots of if...else blocks in my controllers.
I've tried using the deferred api as follows.
worksOrdersApp.factory("WorksOrderService", function ($http, $q) {
var _jobs = [];
var newWorksOrders = function () {
var deferredJobs = $q.defer();
if (_jobs.length === 0) {
return $http.get('/api/WorksOrders/GetNew').success(function(data) {
deferredJobs.resolve(data);
});
} else {
deferredJobs.resolve(_jobs);
}
return deferredJobs.promise;
};
return {
getNewWorksOrders: newWorksOrders,
};
});
Then in my controller it should be a simple case of calling...
WorksOrderService.getNewWorksOrders().then(function (data) {
$scope.values = data;
});
This initially seems to work, the value of data in the service is an array as I'd expect. However when I inspect the value of data in the controller after the call to deferredJobs.resolve() it's a http response object with details of the status code and a separate data.data property which contains the array. This still means I've got to have if...else code in the controller to check if data is an array or a http response.
How do I just pass the array back to my controller?
Typical, as soon as I ask the question I spot the answer.
In the if (_jobs.length === 0) block I'm returning the $http.get instead of executing it and allowing the promise to return the result.
This does what I expect.
if (_jobs.length === 0) {
$http.get('/api/WorksOrders/GetNew').success(function(data) {
_jobs = data; // I also forgot this line, without it _jobs will always be empty and the else block will never execute
deferredJobs.resolve(data);
});
} else {
deferredJobs.resolve(_jobs);
}