Objects returned from promise are undefined - javascript

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)
})

Related

Setting value of variable within promise chain in Javascript

In my web application, there are several components that will need access to the same data (in JSON). To avoid making unnecessary REST calls, I made a module that is supposed make a fetch request and store the result in a variable. On subsequent requests... if the data is available in the module, it will be directly returned (so only one network request is necessary).
For example:
var data_module = function(){
var data; //Module stores the json data in a variable
return{ //Returns an object that contains a public method accessible to external functions
get_json:function(){
if(data){ //If data already exists, then return a Promise object that immediately resolves with data
return Promise.resolve(data);
}
else{ //Else if data does not exist, make fetch request
fetch('/rest/url/endpoint', {credentials:'include'})
.then(function(response){
if(!response.ok){
throw new Error(response.statusText);
}
return response.json(); //Returns json of response
})
.then(function(json){
data = json; //Assigns data the value of json to store the result for subsequent requests
return Promise.resolve(data) //Returns a Promise that resolves with data
});
}
} //Public method that is supposed to provide access to data
}
}(); //Module will automatically execute
Outside of the module, I will try to access data like so:
some_dom_element.onclick = function(){ //Some html element is clicked and we need the data
data_module.get_json().then(function(json){
console.log(json); //However this never gets called
});
}
It does not work. Even though data_module's get_json function returns a Promise, the .then method does not get called outside of data_module. I was wondering if anyone can explain why this happens? (Or provide a general direction of how to modify the solution to achieve the goal of storing json results of fetch requests).
Thanks in advance!
You need to return the fetch
var data_module = (function() {
var data; //Module stores the json data in a variable
return { //Returns an object that contains a public method accessible to external functions
get_json: function() {
if (data) { //If data already exists, then return a Promise object that immediately resolves with data
console.log("** From CACHE **")
return Promise.resolve(data);
} else { //Else if data does not exist, make fetch request
// returning the fetch
return fetch('https://jsonplaceholder.typicode.com/posts/1', {
credentials: 'include'
})
.then(function(response) {
if (!response.ok) {
throw new Error(response.statusText);
}
return response.json(); //Returns json of response
})
.then(function(json) {
data = json;
return Promise.resolve(data) //Returns a Promise that resolves with data
});
}
} //Public method that is supposed to provide access to data
}
}()); //Module will automatically execute
//Outside of the module, I will try to access data like so:
var some_dom_element = document.getElementById("testBt")
some_dom_element.onclick = function() { //Some html element is clicked and we need the data
data_module.get_json().then(function(json) {
console.log(json); //However this never gets called
});
}
<button id="testBt">Test</button>
You could actually make the code a little shorter by storing the promise that response.json() returns and just returning it rather than storing the text and generating a new Promise every time you get it from the cache.
let data_module = (function() {
let data;
return {
get_json: () => {
if (!data) {
data = fetch('https://jsonplaceholder.typicode.com/posts/1', {
credentials: 'include'
})
.then(response => {
if (!response.ok) {
// clear data so next time it tries to contact the server again
data = undefined;
throw new Error(`${response.status}: ${response.statusText}`);
} else {
// return the promise that .json() returns so it is stored
// in data
return response.json();
}
});
}
return data;
}
};
}());
data_module.get_json().then(data => {
console.log(data);
});
data_module.get_json().then(data => {
console.log(data);
});
I used let and arrow functions => since any browser that supports fetch also supports those.

angular js getting out variable from $http is service

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
});
}
}]);

Multiple service http calls using AngularJS

In my controller I am calling a service with the following code:
Service.updateData(data).then(function (result) {
console.log(result);
});
In my service I am using $q to get multiple HTTP requests.
$rootScope.http_1 = $http.get();
$rootScope.http_2 = $http.get();
$q.all([$rootScope.http_1, $rootScope.http_2]).then(function(result) {
console.log(result[0], result[1]);
return result[0], result[1];
});
The code actually works as the http requests are successfully made. However, I get an error in the controller which says: TypeError: Cannot read property 'then' of undefined. I believe this is due the service not returning the promise in the correct way. Any ideas on how to resolve this would be much appreciated?
It looks like you are not returning the promise in updateData Try this:
updateData = function(data) {
$rootScope.http_1 = $http.get();
$rootScope.http_2 = $http.get();
return $q.all([$rootScope.http_1, $rootScope.http_2]);
}
You didn't return the promise, so there is nothing to call .then() on in your controller
You are returning inside the .then() function inside your service.updateData(), which doesn't do very much for you.
If you want to control it all inside the service and return a specific format, try this:
updateData = function(data) {
$rootScope.http_1 = $http.get();
$rootScope.http_2 = $http.get();
var defer = $q.defer();
$q.all([$rootScope.http_1, $rootScope.http_2]).then(function(result){
// process here to get the data how you want it, say in some new var foo
var foo = "some processed data based on result";
defer.resolve(foo);
});
return defer.promise;
}

How to access async data in a factory across multiple controllers without using promises

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;
}
}
})

In AngularJS, how do I automatically update a value fetched via ajax?

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;
}
}
});

Categories