Wait till success or error occur - angularjs

I'm trying use $http.get() in a factory to retrieve a value which will be processed further in a controller.
appFac.factory('CompletedJobs',function($http){
var result;
return{
getJobs : function(webserviceurl){
$http.get(webserviceurl).success(function(data){
result = data;
})
.error(function(err){
result = '';
});
return result;
}
}
})
appCtrl.controller('Ctrl',function(CompletedJobs){
var data = CompletedJobs.getJobs('some url');
// some other logic based on data
});
Due to the asynchronism, by the time the data becomes available inside the factory method, it is of no use to the controller because the factory method has already returned undefined.
Can anyone help me work out how to get the data from the factory method so it can be used by the controller?

In your factory you should return the promise of success or error instead of explicitly returning the result because that will be returned before your promise get resolved. Modify you factory like this.
appFac.factory('CompletedJobs',function($http, $q){
return{
getJobs : function(webserviceurl){
var deferred = $q.defer();
$http.get(webserviceurl).then(function(response){
deferred.resolve(response.data);
return deferred.promise;
});
}
}
})
appCtrl.controller('Ctrl',function(CompletedJobs){
var data = CompletedJobs.getJobs('some url');
// some other logic based on data
});
This should work for you.

In the factory return the promise.
appFac.factory('CompletedJobs',function($http){
return {
getJobs : function(webserviceurl){
//return the promise
return $http.get(webserviceurl);
}
};
In the controller chain from the promise.
appCtrl.controller('Ctrl',function(CompletedJobs){
var promise = CompletedJobs.getJobs('some url');
promise.then( function onFulfilled(result) {
var data = result.data
// some other logic based on data
}).catch( function onRejection(result) {
console.log(result.status);
});
});

Related

$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.

Have multiple calls wait on the same promise in Angular

I have multiple controllers on a page that use the same service, for the sake of example we will call the service USER.
The first time the USER.getUser() is called it does an $http request to GET data on the user. After the call is completed it stores the data in USER.data. If another call is made to USER.getUser() it checks if there is data in USER.data and if there is data it returns that instead of making the call.
My problem is that the calls to USER.getUser() happen so quickly that USER.data does not have any data so it fires the $http call again.
Here is what I have for the user factory right now:
.factory("user", function($http, $q){
return {
getUser: function(){
var that = this;
var deferred = $q.defer();
if(that.data){
deferred.resolve(that.data);
} else {
$http.get("/my/url")
.success(function(res){
that.data = res;
deferred.resolve(that.data);
});
}
return deferred.promise;
}
}
});
I hope my question makes sense. Any help would be much appreciated.
Does this work for you?
.factory("user", function($http, $q) {
var userPromise;
return {
getUser: function () {
if (userPromise) {
return userPromise;
}
userPromise = $http
.get("/my/url")
.then(function(res) {
return res.data;
});
return userPromise;
}
}
})
$http already returns the promise for you, even more, in 2 useful types: success & error. So basically, the option that #kfis offered does NOT catch errors and not flexible.
You could write something simple:
.factory('user', function($http) {
return {
getUser: function() {
$http.get('/my/url/')
.success(function(data) {
return data;
})
.error(function(err) {
// error handler
})
}
}
})

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.

AngularJS service storing $http results to prevent requerying --- is there a better way to do this?

The Setting: I want to have a service that multiple controllers can query for data pulled using $http. The initial solution was to use promises as suggested here.
The Problem: Each time a controller queries the service, the service then returns an $http promise, resulting in multiple queries that just pulls the same data from a remote server, over and over again.
A Solution: The service function returns either data or a promise like below. And it is up to the controller to check and act accordingly.
app.factory('myService', function($http) {
var items = [];
var myService = {
getItems: function() {
// if items has content, return items; otherwise, return promise.
if (items.length > 0) {
return items;
} else {
var promise = $http.get('test.json').then(function (response) {
// fill up items with result, so next query just returns items.
for(var i=0;i<response.data.length;i++){
items.push(response.data[i]);
}
return items;
});
// Return the promise to the controller
return promise;
}
};
return myService;
});
So when a controller needs that data, the controller just does something like this:
app.controller('MainCtrl', function( myService,$scope) {
var promiseOrData = myService.async();
// Check whether result is a promise or data.
if ( typeof promiseOrData.then === 'function'){
// It's a promise. Use then().
promiseOrData.then( function(data ){
$scope.data = data;
});
} else {
// It's data.
$scope.data = data;
}
});
So the question is: Is there a better way of doing this? With many controllers, this method would have a lot of duplicate code. Ideally, the controllers will just query the service for data directly.
Thanks!
$http returns a promise, we can use that instead of creating a new one with $q. Once the promise is resolved, we can keep returning it.
.factory('myService', ['$http','$q', function($http, $q) {
var items = [];
var last_request_failed = true;
var promise = undefined;
return {
getItems: function() {
if(!promise || last_request_failed) {
promise = $http.get('test.json').then(
function(response) {
last_request_failed = false;
items = response.data;
return items;
},function(response) { // error
last_request_failed = true;
return $q.reject(response);
});
}
return promise;
},
};
}])
In your controller:
myService.getItems().then(
function(data) { $scope.data = data; }
);
Create your own promise that resolves to either the cached data or the fetched data.
app.factory('myService', function($http, $q) {
var items = [];
var myService = {
getItems: function() {
var deferred = $q.defer();
if (items.length > 0) {
//resolve the promise with the cached items
deferred.resolve(items);
} else {
$http.get('test.json').then(function (response) {
// fill up items with result, so next query just returns items.
for(var i=0;i<response.data.length;i++){
items.push(response.data[i]);
}
//resolve the promise with the items retrieved
deferred.resolve(items);
},function(response){
//something went wrong reject the promise with a message
deferred.reject("Could not retrieve data!");
});
}
// Return the promise to the controller
return deferred.promise;
};
return myService;
});
Then consume the promise in your controller.
app.controller('MainCtrl', function( myService,$scope) {
var promiseOrData = myService.getItems();
promiseOrData.then( function(data){
$scope.data = data;
},
function(data){
// should log "Could not retrieve data!"
console.log(data)
});
});

Passing a parameter to async service method from controller

I am new to angularjs so I am struggling to pass a parameter to service method from controller.
My controller looks like this:
userControllers.controller('StartController', function(startService,$scope) {
var server='http://localhost:8080/terminal/1';
// Call the async method and then do stuff with what is returned inside our own then function
startService.async().then(function(d) {
$scope.message = d;
});
});
Service method:
myService.factory('startService', function($http) {
var startService = {
async: function () {
// $http returns a promise, which has a then function, which also returns a promise
var promise = $http.get('http://localhost:8080/terminal/1').then(function (response) {
// The then function here is an opportunity to modify the response
console.log(response.headers('link'));
// The return value gets picked up by the then in the controller.
return response;
});
// Return the promise to the controller
return promise;
}
};
return startService;
});
And this code works fine. Now I want to pass variable 'server' from controller to be used in service instead of link. Any idea how to do that? How to use more than one variable in service function call?
Updated code is in between ** **
userControllers.controller('StartController', function(startService,$scope) {
var server='http://localhost:8080/terminal/1';
// Call the async method and then do stuff with what is returned inside our own then function
**startService.async(server).then(function(d) {**
$scope.message = d;
});
});
myService.factory('startService', function($http) {
var startService = {
async: **function (server) {**
// $http returns a promise, which has a then function, which also returns a promise
var promise = **$http.get(server)**.then(function (response) {
// The then function here is an opportunity to modify the response
console.log(response.headers('link'));
// The return value gets picked up by the then in the controller.
return response;
});
// Return the promise to the controller
return promise;
}
};
return startService;
});

Resources