AngularJS $http success callback data return - angularjs

Hello everyone :) Here is the problem. I'm making an angular app with:
a factory to access to an api with $http that retrieves an array of objects from a server
getObjectsFromApi : function(){
return $http({
url: 'http://path/to/the/api/',
method: 'GET',
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
}
})
},
and a service to compute retrieved data and inject it to controllers
this.getObjectsFromService = function(){
var objects = [];
ObjectFactory.getObject()
.success(function(data, status){
console.log("Successfully got objects !");
objects = data;
})
.error(function(data, status){
console.log("Fail... :-(");
});
return objects;
};
The problem is that when I return objects, it doesn't return any data. How can I do to return $http callback data within this getObjectsFromService function ?
Thank you for helping !

You must use promises, something like this should do the trick
this.getObjectsFromService = function(){
var defer = $q.defer();
ObjectFactory.getObject()
.then(function(data){
console.log("Successfully got objects !");
defer.resolve(data);
})
.catch(function(data){
console.log("Fail... :-(");
defer.reject(data);
});
return defer.promise;
};
And now you can use this function somewhere else like this:
var foo = function() {
var objects = [];
this.getObjectsFromService().then(function(data) {
objects = data;
//do rest of your manipulation of objects array here
});
}
There is no other way to return the objects, it's not possible because $http is asynchronous. You'll have to rework the rest of the code and adapt it to this

var req = ObjectFactory.getObject();
req.success(function(data) {...}
req.error(function(data) {...}
This will do it for ya
The variable req will then be the promise sent back from the factory.
Edit
Keep in mind that your data will not be changes until the promise is resolved, so if you try to console.log(objects) before then, it will be empty.

The http request is async, which means that it completes after you return objects. Return the promise instead:
this.getObjectsFromService = function(){
return ObjectFactory.getObject().catch(function(){
console.log("Fail... :-(");
});
}
Then
service.getObjectsFromService().then(function(resp) {
console.log("Successfully got objects !", resp.data);
});

Your code is asynchronous. When you return objects, you return your initial empty array. You can't return directly your object, but instead you need to return a promise (see 3 possibilities below)
By creating a promise
var deferred = $q.defer();
ObjectFactory.getObjectsFromApi()
.success(function(data, status){
console.log("Successfully got objects !");
// Do stuff and resolve promise
deferred.resolve(data);
})
.error(function(data, status){
console.log("Fail... :-("));
// Do stuff and reject promise
deferred.reject(data)
});
return deferred.promise;
You can use promise chaining (use .then rather than .success and .error):
Note: when using then with success and error callbacks rather than success and error methods, you have only 1 argument which is the response object
return ObjectFactory.getObjectsFromApi()
.then(function(response){
console.log("Successfully got objects !");
// Do stuff and chain full response headers or data
return responseHeaders;
// or
// return responseHeaders.data;
}, function(responseHeaders){
console.log("Fail... :-("));
// Do stuff and chain error (full response headers or data)
return $q.reject(responseHeaders)
// return $q.reject(responseHeaders.data);
});
Or if you have no business logic, or no reason to intervene in your factory, simply return your $http call directly:
return ObjectFactory.getObjectsFromApi();

Angular $resource
Factory
getObjectsFromApi : function(){
return $resource('http://path/to/the/api/', {},
{
get: {
method: 'GET',
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
}
}
})
},
Service
this.getObjectsFromService = function(){
var objects = [];
ObjectFactory.get().$promise.then(function(data) {
objects = data;
return objects;
}).error(function(err) {
throw err;
});
};

Related

Type IHttpPromiseCallbackArg<{}> is not assignable to type 'string' [duplicate]

I'm finding it hard to understand the "deferred antipattern". I think I understand it in principal but I haven't seen a super simple example of what a service, with a differed promise and one with antipattern, so I figured I'd try and make my own but seeing as how I'm not super in the know about it I'd get some clarification first.
I have the below in a factory (SomeFactory):
//url = 'data.json';
return {
getData: function(){
var deferred = $q.defer();
$http.get(destinationFactory.url)
.then(function (response) {
if (typeof response.data === 'object') {
deferred.resolve(response.data);
} else {
return deferred.reject(response.data);
}
})
.catch(function (error) {
deferred.reject(error);
});
return deferred.promise;
}
The reason I am checking its an object is just to add a simple layer of validation onto the $http.get()
And below, in my directive:
this.var = SomeFactory.getData()
.then(function(response) {
//some variable = response;
})
.catch(function(response) {
//Do error handling here
});
Now to my uderstanding, this is an antipattern. Because the original deferred promise catches the error and simply swallows it. It doesn't return the error so when this "getData" method is called I have do another catch to grab the error.
If this is NOT an antipattern, then can someone explain why both require a "callback" of sorts? When I first started writing this factory/directive I anticipated having to do a deffered promise somewhere, but I didn't anticipate having to .catch() on both sides (aka I was sort of thinking I could get the factory to return the response or the error if I did a SomeFactory.getData()
Is this a “Deferred Antipattern”?
Yes, it is. 'Deferred anti-pattern' happens when a new redundant deferred object is created to be resolved from inside a promise chain. In your case you are using $q to return a promise for something that implicitly returns a promise. You already have a Promise object($http service itself returns a promise), so you just need to return it!
Here's the super simple example of what a service, with a deferred promise and one with antipattern look like,
This is anti-pattern
app.factory("SomeFactory",['$http','$q']){
return {
getData: function(){
var deferred = $q.defer();
$http.get(destinationFactory.url)
.then(function (response) {
deferred.resolve(response.data);
})
.catch(function (error) {
deferred.reject(error);
});
return deferred.promise;
}
}
}])
This is what you should do
app.factory("SomeFactory",['$http']){
return {
getData: function(){
//$http itself returns a promise
return $http.get(destinationFactory.url);
}
}
while both of them are consumed in the same way.
this.var = SomeFactory.getData()
.then(function(response) {
//some variable = response;
},function(response) {
//Do error handling here
});
There's nothing wrong with either examples(atleast syntactically)..but first one is redundant..and not needed!
Hope it helps :)
I would say that it is the classic deferred anti-pattern because you are creating needless deferred objects. However, you are adding some value to the chain (with your validation). Typically, IMO, the anti-pattern is particularly bad when deferred objects are created for very little or no benefit.
So, the code could be much simpler.
$q promises have a little documented feature of automatically wrapping anything returned inside a promise in a promise (using $q.when). In most cases this means that you shouldn't have to manually create a deferred:
var deferred = $q.defer();
However, that is how the documentation demonstrates how to use promises with $q.
So, you can change your code to this:
return {
getData: function(){
return $http.get(destinationFactory.url)
.then(function (response) {
if (typeof response.data === 'object') {
return response.data;
} else {
throw new Error('Error message here');
}
});
// no need to catch and just re-throw
});
}
Using the $q constructor is a deferred anti-pattern
ANTI-PATTERN
vm.download = function() {
var url = "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf";
return $q(function(resolve, reject) {
var req = {
method: 'POST',
url: url,
responseType: 'arraybuffer'
};
$http(req).then(function(response) {
resolve(response.data);
}, function(error) {
reject(error);
});
});
}
CORRECT
vm.download = function() {
var url = "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf";
var req = {
method: 'POST',
url: url,
responseType: 'arraybuffer'
};
return $http(req).then(function(response) {
return response.data;
});
}
The $http service already returns a promise. Using the $q constructor is unnecessary and error prone.

Return chain of promises in AngularJS [duplicate]

I'm finding it hard to understand the "deferred antipattern". I think I understand it in principal but I haven't seen a super simple example of what a service, with a differed promise and one with antipattern, so I figured I'd try and make my own but seeing as how I'm not super in the know about it I'd get some clarification first.
I have the below in a factory (SomeFactory):
//url = 'data.json';
return {
getData: function(){
var deferred = $q.defer();
$http.get(destinationFactory.url)
.then(function (response) {
if (typeof response.data === 'object') {
deferred.resolve(response.data);
} else {
return deferred.reject(response.data);
}
})
.catch(function (error) {
deferred.reject(error);
});
return deferred.promise;
}
The reason I am checking its an object is just to add a simple layer of validation onto the $http.get()
And below, in my directive:
this.var = SomeFactory.getData()
.then(function(response) {
//some variable = response;
})
.catch(function(response) {
//Do error handling here
});
Now to my uderstanding, this is an antipattern. Because the original deferred promise catches the error and simply swallows it. It doesn't return the error so when this "getData" method is called I have do another catch to grab the error.
If this is NOT an antipattern, then can someone explain why both require a "callback" of sorts? When I first started writing this factory/directive I anticipated having to do a deffered promise somewhere, but I didn't anticipate having to .catch() on both sides (aka I was sort of thinking I could get the factory to return the response or the error if I did a SomeFactory.getData()
Is this a “Deferred Antipattern”?
Yes, it is. 'Deferred anti-pattern' happens when a new redundant deferred object is created to be resolved from inside a promise chain. In your case you are using $q to return a promise for something that implicitly returns a promise. You already have a Promise object($http service itself returns a promise), so you just need to return it!
Here's the super simple example of what a service, with a deferred promise and one with antipattern look like,
This is anti-pattern
app.factory("SomeFactory",['$http','$q']){
return {
getData: function(){
var deferred = $q.defer();
$http.get(destinationFactory.url)
.then(function (response) {
deferred.resolve(response.data);
})
.catch(function (error) {
deferred.reject(error);
});
return deferred.promise;
}
}
}])
This is what you should do
app.factory("SomeFactory",['$http']){
return {
getData: function(){
//$http itself returns a promise
return $http.get(destinationFactory.url);
}
}
while both of them are consumed in the same way.
this.var = SomeFactory.getData()
.then(function(response) {
//some variable = response;
},function(response) {
//Do error handling here
});
There's nothing wrong with either examples(atleast syntactically)..but first one is redundant..and not needed!
Hope it helps :)
I would say that it is the classic deferred anti-pattern because you are creating needless deferred objects. However, you are adding some value to the chain (with your validation). Typically, IMO, the anti-pattern is particularly bad when deferred objects are created for very little or no benefit.
So, the code could be much simpler.
$q promises have a little documented feature of automatically wrapping anything returned inside a promise in a promise (using $q.when). In most cases this means that you shouldn't have to manually create a deferred:
var deferred = $q.defer();
However, that is how the documentation demonstrates how to use promises with $q.
So, you can change your code to this:
return {
getData: function(){
return $http.get(destinationFactory.url)
.then(function (response) {
if (typeof response.data === 'object') {
return response.data;
} else {
throw new Error('Error message here');
}
});
// no need to catch and just re-throw
});
}
Using the $q constructor is a deferred anti-pattern
ANTI-PATTERN
vm.download = function() {
var url = "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf";
return $q(function(resolve, reject) {
var req = {
method: 'POST',
url: url,
responseType: 'arraybuffer'
};
$http(req).then(function(response) {
resolve(response.data);
}, function(error) {
reject(error);
});
});
}
CORRECT
vm.download = function() {
var url = "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf";
var req = {
method: 'POST',
url: url,
responseType: 'arraybuffer'
};
return $http(req).then(function(response) {
return response.data;
});
}
The $http service already returns a promise. Using the $q constructor is unnecessary and error prone.

$http request wrapped in Service returns the whole response in resolve state Provider

I have a state defined like this:
.state('list', {
url: '/list',
controller: 'ctrl',
resolve: {
data: ['DataService', function(DataService) {
return DataService.getList();
}]
}
})
The getList of DataService makes the http request:
var httpRequest = $http(categoryRequest);
httpRequest.then(function (response) {
return response.data;
})
.catch(function (error) {
console.log('could not get categories from server');
});
return httpRequest;
In controller I just assign the list to its list property:
function ctrl(data) {
this.list = data.data;
}
The problem:
No matter what I return in success callback of http request, I always get the whole response in resolve of state provider.
So I have to do data.data in controller to get the data from response.
Questions:
Is my assumption true that I will always get the whole reponse in resolve?
How to get just the data form response that I do not have to get it in controller.
Best regards,
var httpRequest = $http(categoryRequest);
So httpRequest is a Promise<Response>
httpRequest.then(function (response) {
This creates another promise, but this new promise is not assigned to anything.
return httpRequest;
This returns the original Promise<Response>.
You want
httpRequest = httpRequest.then(function (response) {
Or simply
return httpRequest.then(function (response) {
So that what you return is the new promise.
To give you a simpler analog example, your code is similar to
var a = 1;
a + 1;
return a;
That returns 1, not 2.
To return 2, you need
var a = 1;
a = a + 1;
return a;
or
var a = 1;
return a + 1;
There are two problems. First, the service is returning the original httpPromise and not the promise derived from the original promise. Second, the error handler is converting the rejection to a success.
var httpRequest = $http(categoryRequest);
//httpRequest.then(function (response) {
var derivedPromise = httpRequest.then(function onSuccess(response) {
//return to chain data
return response.data;
}).catch(function onReject(error) {
console.log('could not get categories from server');
//IMPORTANT to avoid conversion
throw error;
//OR
//return $q.reject(error);
});
//return httpRequest;
return derivedPromise;
The .then method of a promise returns a new promise derived from the original promise. It does not mutate the original promise.
A common problem is the omission of a throw or return $q.reject statement from a rejection handler. Functions without such statements return a value of undefined which will convert a rejection to a success which resolves as undefined.

synchronous http call in angularJS

I have the following scenario, I need data from a particular url. I have written a function which takes parameter 'url'. Inside the function I have the $http.get method which makes a call to the url. The data is to be returned to the calling function
var getData = function (url) {
var data = "";
$http.get(url)
.success( function(response, status, headers, config) {
data = response;
})
.error(function(errResp) {
console.log("error fetching url");
});
return data;
}
The problem is as follows, $http.get is asynchronous, before the response is fetched, the function returns. Therefore the calling function gets the data as empty string. How do I force the function not to return until the data has been fetched from the url?
Take a look at promises to overcome such issues, because they are used all over the place, in angular world.
You need to use $q
var getData = function (url) {
var data = "";
var deferred = $q.defer();
$http.get(url)
.success( function(response, status, headers, config) {
deferred.resolve(response);
})
.error(function(errResp) {
deferred.reject({ message: "Really bad" });
});
return deferred.promise;
}
Here's a nice article on promises and $q
UPDATE:
FYI, $http service itself returns a promise, so $q is not necessarily required in this scenario(and hence an anti-pattern).
But do not let this be the reason to skip reading about $q and promises.
So the above code is equivalent to following:
var getData = function (url) {
var data = "";
return $http.get(url);
}
You can use $q.all() method also to solve this problem
var requestPromise = [];
var getData = function (url) {
var data = "";
var httpPromise = $http.get(url)
.success( function(response, status, headers, config) {
data = response;
})
.error(function(errResp) {
console.log("error fetching url");
});
requestPromise.push(httpPromise);
}
in the calling function
$q.all(requestPromise).then(function(data) {
//this is entered only after http.get is successful
});
make sure to inject $q as a dependency. Hope it helps
You function seems redundant. Just use $http.get(url), since you aren't really doing anything else before you use it anyway.
var url = 'foo/bar';
$http
.get(url)
.success( function(response, status, headers, config) {
$scope.data = response;
})
.error(function(errResp) {
console.log("error fetching url");
});
Or if you need to access the promise later just assign it to variable;
var promise = $http.get(url);
// some other code..
promise.then(function(data){
//.. do something with data
});
A typical way to do what you want is like so:
var getData = function(url, callback) {
$http.get(url).success(function(response) {
callback && callback(response);
});
};
Used like:
getData('/endpoint', function(data) {
console.log(data);
});

Angular js service returns function objects when called from a controller?

var newservices = angular.module('newservices', []);
newservices.service('newservice', function ($http) {
return{
newdata: function(parameter){
return $http.get('/devicedetails/'+parameter).success(function(data) {
console.log(data)
return data
});
},
}
});
The above service is included in one of my controllers
data=newService.newdata($scope.dummy)
console.log(data)
while trying to print data what i get is $http function object as shown below
Object {then: function, catch: function, finally: function, success: function, error: function}
why is this so??
What you see is not an error. It's a Promise.
You did an $http GET request, which is asynchronous. $http.getreturns a promise that will be resolved when the remote request is completed. In that moment, you'll get the final value.
See this example, where getShops would be your method newData
this.getShop = function (id, lang) {
var promise = $http.get(appRoot + 'model/shops_' + lang + '.json');
return promise;
};
In a controller you can use it like this:
Shops.getShop($routeParams.id).then(function (response) {
console.log("data is", response.data);
$scope.shop = response.data[$routeParams.id];
});
When the data is ready, assign it to a scope.
In your case:
var data;
newService.newdata($scope.dummy).then(function (response) {
data = response.data;
});
Your service is returnig a promise
You should use some what like this, not tested though it should work.
data = newService.newdata($scope.dummy).then(function (response) {
return response.data;
},
function (error) {
return error;
});
You are using it wrong.
This work in promises. so in you controller you need to consume the promisses.
newService.newData($scope.dummy)
.then(function (data){
$scope.data = data;
console.log(data);
});
Try this.

Resources