update q defer promise before returning - angularjs

I call getBookIDs from factory and by using the result I call getBookInfo from the same factory. but in the Console.log(bookInfo) it shows me the result of previous call!
how can I update the deferred.promise value before returning??
this is my controller
angular.module('myApp.products',[])
.controller('productController', function ($scope , MainFactory , $location) {
function getBookInfo(bookIDs){
MainFactory.getBookList(bookIDs)
.then(function (bookInfo) {
console.log(bookInfo)
})
}
MainFactory.getBookIDs()
.then(function (result) {
$scope.bookIDList = result;
getBookInfo($scope.bookIDList);
});
});
and this is my factory
app = angular.module('myApp');
app.factory("MainFactory", ['$soap', '$http', '$q', function ($soap, $http, $q) {
var viewFactory = {};
var deferred = $q.defer();
viewFactory.getBookIDs = function () {
//var bookIDs = [];
$http({
url: 'http://127.0.0.1/client.php?fn=getBooks',
method: "GET"
}).then(function success(response) {
deferred.resolve(response.data.result);
}, function myError(error) {
console.log('error', error);
});
return deferred.promise;
};
viewFactory.getBookList = function (bookIDs) {
$http({
url: 'http://127.0.0.1/client.php?fn=getBooksInfo&p1=' + bookIDs,
method: "GET"
}).then(function success(response) {
deferred.resolve(response.data.result);
}, function myError(error) {
deferred.reject(error);
});
return deferred.promise;
};
return viewFactory;
}]);

You should return a new promise for each of your service methods:
app.factory("MainFactory", ['$soap', '$http', '$q', function ($soap, $http, $q) {
var viewFactory = {};
viewFactory.getBookIDs = function () {
var deferred = $q.defer();
//var bookIDs = [];
$http({
url: 'http://127.0.0.1/client.php?fn=getBooks',
method: "GET"
}).then(function success(response) {
deferred.resolve(response.data.result);
}, function myError(error) {
console.log('error', error);
});
return deferred.promise;
};
viewFactory.getBookList = function (bookIDs) {
var deferred = $q.defer();
$http({
url: 'http://127.0.0.1/client.php?fn=getBooksInfo&p1=' + bookIDs,
method: "GET"
}).then(function success(response) {
deferred.resolve(response.data.result);
}, function myError(error) {
deferred.reject(error);
});
return deferred.promise;
};
return viewFactory;
}]);
Promises should not be reused (unless you wish to perform multiples tasks triggering the same resolve/reject... still, you should explicitly implement a promise aggregator for that, I think).

All angular services are singletons, so i guess the reason you got this bug is getBookIDs and getBookList share the same deferred
try change your factory to
app.factory("MainFactory", ['$soap', '$http', '$q', function ($soap, $http, $q) {
var viewFactory = {};
viewFactory.getBookIDs = function () {
//var bookIDs = [];
var deferred = $q.defer();
$http({
url: 'http://127.0.0.1/client.php?fn=getBooks',
method: "GET"
}).then(function success(response) {
deferred.resolve(response.data.result);
}, function myError(error) {
console.log('error', error);
});
return deferred.promise;
};
viewFactory.getBookList = function (bookIDs) {
var deferred = $q.defer();
$http({
url: 'http://127.0.0.1/client.php?fn=getBooksInfo&p1=' + bookIDs,
method: "GET"
}).then(function success(response) {
deferred.resolve(response.data.result);
}, function myError(error) {
deferred.reject(error);
});
return deferred.promise;
};
return viewFactory;
}]);

Related

Return deferred.promise 2 services back

I have 3 services.
HttpSender - It controls the $http request
app.service("HttpSender", ["$http", "$q", function ($http, $q) {
this.send = function (path, method, params) {
var deferred = $q.defer();
$http({
url: path,
method: method,
params: params
}).then(function successCallback(response) {
deferred.resolve(response.data);
}, function errorCallback(response) {
deferred.reject(response);
});
return deferred.promise;
};
this.sendRequestWithFile = function (path, method, params) {
//todo check if needed
};
}]);
Api - controls all the api/ access token processes
service("API", ["HttpSender", "$q", 'WindowOpen', function(HttpSender, $q, WindowOpen){
var self = this;
var API = {};
API.requestTypes = {
GetMethod: "GET",
PostMethod: "POST",
DeleteMethod: "DELETE",
PutMethod: "PUT"
};
API.sendRequest = function (path, method, parameters, isCheckAccessToken)
{
path = ServersConfig.getApiServerUrl() + path;
parameters.access_token = getAccessToken();
HttpSender.send(path, method, parameters);
};
return API;
}]);
Api - Endpoint which activate the api request
app.factory('SelectedEndpoint', ['API',
function (API) {
var getPath = function (campaign) {
return "/campaigns/" + campaign.id + '/content/selected';
};
return {
get: function (campaign) {
API.sendRequest(getPath(campaign), API.requestTypes.GetMethod, {}, true).then(function (content) {
});
}
};
}]);
How can return the deferred.promise to the endpoint function so the then will get the answer? The following process only works if i add then also in the api factory the then return it to the endpoint
How can return the deferred.promise to the endpoint function so the then will get the answer?
By properly returning promises on each step.
1. HttpSender service. Do not use deferred here, just return promise directly:
app.service("HttpSender", ["$http", "$q", function($http, $q) {
this.send = function(path, method, params) {
return $http({
url: path,
method: method,
params: params
}).then(function successCallback(response) {
return response.data;
});
};
}]);
2. Api service. Make sure you return previous promise with return HttpSender.send(path, method, parameters);:
service("API", ["HttpSender", "$q", 'WindowOpen', function(HttpSender, $q, WindowOpen) {
var self = this;
var API = {};
API.requestTypes = {
GetMethod: "GET",
PostMethod: "POST",
DeleteMethod: "DELETE",
PutMethod: "PUT"
};
API.sendRequest = function(path, method, parameters, isCheckAccessToken) {
path = ServersConfig.getApiServerUrl() + path;
parameters.access_token = getAccessToken();
return HttpSender.send(path, method, parameters); // note return promise
};
return API;
}]);

AngularJS defer return until completed

I have tried to build a service that will return a $resource after the service has authenticated.
I have done it like this:
.factory('MoltinApi', ['$q', '$resource', '$http', 'moltin_options', 'moltin_auth', function ($q, $resource, $http, options, authData) {
var api = $resource(options.url + options.version + '/:path', {
path: '#path'
});
var authenticate = function () {
if (!options.publicKey)
return;
var deferred = $q.defer();
var request = {
method: 'POST',
url: options.url + 'oauth/access_token',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: "grant_type=implicit&client_id=" + options.publicKey
};
$http(request).success(function (response) {
authData = response;
deferred.resolve(api);
});
return deferred.promise;
};
return authenticate();
}])
But I can not call the resource in my controller:
.controller('HomeController', ['MoltinApi', function (moltin) {
var self = this;
moltin.get({ path: 'categories' }, function (categories) {
console.log(categories);
});
}]);
it just states that 'undefined is not a function'.
Can someone tell me what I am doing wrong?
Update 1
So after playing with the solution that was suggested, this is the outcome.
angular.module('moltin', ['ngCookies'])
// ---
// SERVICES.
// ---
.factory('MoltinApi', ['$cookies', '$q', '$resource', '$http', 'moltin_options', function ($cookies, $q, $resource, $http, options) {
var api = $resource(options.url + options.version + '/:path', {
path: '#path'
});
var authenticate = function () {
if (!options.publicKey)
return;
var deferred = $q.defer();
var authData = angular.fromJson($cookies.authData);
if (!authData) {
console.log('from api');
var request = {
method: 'POST',
url: options.url + 'oauth/access_token',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: "grant_type=implicit&client_id=" + options.publicKey
};
deferred.resolve($http(request).success(function (response) {
$cookies.authData = angular.toJson(response);
setHeaders(response.access_token);
}));
} else {
console.log('from cookie');
deferred.resolve(setHeaders(authData.access_token));
}
return deferred.promise;
};
var setHeaders = function (token) {
$http.defaults.headers.common['Authorization'] = 'Bearer ' + token;
}
return authenticate().then(function (response) {
return api;
});
}]);
and to call it I have to do this:
.controller('HomeController', ['MoltinApi', function (moltin) {
var self = this;
moltin.then(function (api) {
api.get({ path: 'categories' }, function (categories) {
console.log(categories);
self.sports = categories.result;
});
});
}]);
but what I would like to do is this:
.controller('HomeController', ['MoltinApi', function (moltin) {
var self = this;
moltin.get({ path: 'categories' }, function (categories) {
console.log(categories);
}, function (error) {
console.log(error);
});
}]);
As you can see, the service is checking to see if we have authenticated before returning the API. Once it has authenticated then the API is returned and the user can then call the api without having to authenticate again.
Can someone help me refactor this service so I can call it without having to moltin.then()?
You are returning the authenticate function call in the MoltinApi factory, so you are returning the promise. And the method get doesn't exist in the promise

Why does an angular $http call that fails repeat itself infinitely?

I have:
this.isAuthenticated = function() {
var deferred;
deferred = $q.defer();
$http({
url: "" + endpoints.authService + "api/v1/session/check",
method: 'GET',
withCredentials: true
}).success(function(response) {
var user;
if (response.authenticated === true) {
user = response;
}
return deferred.resolve(response);
}).error(function(data, status, headers, config) {
deferred.reject(data);
return $rootScope.$broadcast('api-error', 'Cannot access authentication service');
});
return deferred.promise;
};
Assuming that the endpoint is down, apparently it tries to do the call infinitely. Is this some known Angular behavior? And can I disable it?
No factory making http call when your controller invoking that call please see here: http://plnkr.co/edit/u7YSD8gkbOSKN32SU62P?p=preview. It's rather you controller keeping call factory function.
var app = angular.module('plunker', []);
app.factory('dataService', function($http, $q, $rootScope) {
var endpoints = {
authService: "ttest"
};
this.isAuthenticated = function() {
var deferred;
deferred = $q.defer();
$http({
url: "" + endpoints.authService + "api/v1/session/check",
method: 'GET',
withCredentials: true
}).success(function(response) {
var user;
if (response.authenticated === true) {
user = response;
}
return deferred.resolve(response);
}).error(function(data, status, headers, config) {
deferred.reject(data);
return $rootScope.$broadcast('api-error', 'Cannot access authentication service');
});
return deferred.promise;
};
return this;
});
app.controller('MainCtrl', function($scope, dataService) {
$scope.$on('api-error', function(a, b){
alert(b);
});
dataService.isAuthenticated();
});
just put this deferred.resolve(response); instead of return deferred.resolve(response);
and also remove return from :
return $rootScope.$broadcast('api-error', 'Cannot access authentication service');

Angular controller won't update the view for some reason

I have a service that looks like:
myApp.service('peerService', [
'$http', function($http) {
this.getPeers = function() {
return $http({
url: '/api/v1/peers',
method: 'GET'
});
};
}
]);
In my controller, I have:
myApp.controller('PeerComparisonController', [
'$scope', '$rootScope', 'peerService', function($scope, $rootScope, peerService) {
$scope.init = function() {
$rootScope.pageTitle = 'Peer Comparison';
return $scope.peers = [];
};
$scope.getPeers = function() {
return peerService.getPeers().then(function(response) {
console.log(response);
return $scope.peers = response.data;
});
};
return $scope.init();
}
]);
My view has {{ peers }} in it. This does not get updated when the service returns. I've tried:
peerService.getPeers().then(function(response) {
return $scope.$apply(function() {
return $scope.peers = response.data;
});
});
And that also doesn't work. So any ideas?
you try something like this
myApp.service('peerService', [
'$http', function($http) {
this.getPeers = function() {
return $http({method: 'GET', url:'/api/v1/peers'}).
then(function(response) {
return response.data;
});
};
}
]);
or
myApp.service('peerService', ['$http', function ($http) {
this.getPeers = function () {
var deferred = $q.defer();
$http({ method: 'GET', url: '/api/v1/peers' }).
then(function (response) {
deferred.resolve(response.data);
});
return deferred.promise;
};
}
]);

Return interdependent async promises in $routeProvider resolve

Consider the code:
var myApp = angular.module('myApp', []);
The routes:
myApp.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/', {
templateUrl: 'app.html',
controller:myAppController,
resolve:{
resolveData:function(Resolver){
return Resolver();
}
}
});
});
Resolve:
myApp.factory('Resolver', ['$http', function($http){
return function(){
return $http({url: '/someurl',method: "GET"}).then(function(data) {
// dependent call 1
$http({url: '/someotherurl',method: "GET" }).then(function(data) {
});
// dependent call 2
$http({url: '/someanotherurl',method: "GET" }).then(function(data) {
});
});
}
}]);
Above I have nested 2 calls inside one as they are dependent on the data returned by the parent call.
What I want to do: return the Resolver when all of them have completed and not just the parent call.
I cannot use $q.all() because 2 of the calls are dependent of the first call.
In short, myAppController must be loaded only after all the 3 calls have completed.
You should be using chaining promise and $q service to solve your problem .Just use the below sample code it should work
myApp.factory('Resolver', ['$http','$q', function ($http,$q) {
return function () {
var deferred = $q.defer();
$http({ url: '/someurl', method: "GET" }).then(function (data) {
return $http({ url: '/someurl', method: "GET" })
}).then(function (data) {
return $http({ url: '/someanotherurl', method: "GET" })
}).then(function (data) {
deferred.resolve(data);
});
return deferred.promise;
}
}]);
This works for me:
resolve : {
message: function($q, $route, Restangular) {
var msgId = $route.current.params.msgId;
var deferred = $q.defer();
Restangular.one('message', msgId).get().then(function(message) {
Restangular.one('file', message.audioFile.id).get().then(function (blob) {
message.blob = blob;
deferred.resolve(message);
});
});
return deferred.promise;
}
}

Resources