Promise from service is not getting resolved in jasmine - javascript

I have a function in service which returns a promise.
When I mock $http post, the promise always goes to rejected state
beforeEach(inject(function (testService, constantService, $http, $injector, $rootScope, $q) {
$httpBackend = $injector.get("$httpBackend");
_testService_ = testService;
_constantService_ = constantService;
_$rootScope_ = $rootScope;
$scope = $rootScope.$new();
deferred = $q.defer();
}));
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it('should post user form data and get an object back', function() {
var response;
var formData = {};
$httpBackend.when("POST", "http://localhost/postform").respond({
data: {
responseStatus:true,
reportPath :"somepath"
}
});
var promise = _testService_.postData("http://localhost" + _constantService_.getWebService('link.dataParameterURL.POST'), formData);
promise.then(function(res) {
response = res;
}, function(errorData){
console.log(errorData)
});
_$rootScope_.$apply();
$httpBackend.flush();
expect(response).toBeDefined();
});
Response is always undefined.
If I use the below code the promise gets resolved.
var s = $http.post("http://localhost" + constantService.getWebService('link.dataParameterURL.POST'), formData).success(function (res) {
if(res!=undefined && res.responseStatus!=undefined && res.responseStatus === true) {
d.resolve(res);
}else{
d.reject();
}
}).error(function (e,status) {
d.reject(e)
});
s.then(function(res) {
response = res;
}, function(errorData){
console.log(errorData)
});
TestService.js
this.postData = function(url, dataToPOST) {
var d = $q.defer();
$http.post(url, dataToPOST).success(function (res) {
if(res!=undefined && res.responseStatus!=undefined && res.responseStatus === true) {
d.resolve(res);
}else{
d.reject();
}
}).error(function (e,status) {
d.reject(e)
});
return d.promise;
};

Try to add the response code to your code:
$httpBackend.when("POST", "http://localhost/postform").respond(200, {
data: {
responseStatus:true,
reportPath :"somepath"
}
});

Related

Issue with AngularJS promises within a factory

I have an angularjs factory like this:
'use strict';
angular.module('frontRplApp')
.factory('paymentService', function ($rootScope, $http, config, tools) {
var urlBase = config.baseUrl;
var paymentService = {
response: {},
create: function () {
var args = {};
return $http.post(urlBase + 'api/investor/payment/create', args);
}
});
And I intend to use it inside a controller like this (the important issue is being to do something different if all went well or if there was an error)
$scope.order = function () {
console.log('PaymentCashCtrl.order');
$scope.disabledButtons.submitCashOrder = true;
paymentService.create()
.then(
function (response) {
// do something with response
}, function (error) {
// do something with an error
}));
};
However my issue is that Id like to update some of the paymentService fields as the response of the $http.post is resolved and then return the promise so that the function(response) and function(error) callbacks in the controller keep working.
I tried with something like:
return $http.post(urlBase + 'api/investor/payment/create', args)
.then(function(response){
console.log(response);
this.response = response;
return response;
});
But it doesnt work since the function(error) handler in the controller is never called.
I want to use my handlers in the controller but also make some updates when the $http.post response is resolved.
Thanks.
in the factory, you need to return the functions paymentService object. also, don't resolve the promise inside the factory. resolve it in the controller.
.factory('paymentService', function($rootScope, $http, config, tools) {
var urlBase = config.baseUrl;
var paymentService = {
response: {},
create: function() {
var args = {};
return $http.post(urlBase + 'api/investor/payment/create', args);
}
}
return paymentService;
});
$scope.order = function() {
console.log('PaymentCashCtrl.order');
$scope.disabledButtons.submitCashOrder = true;
paymentService.create()
.then(
function(response) {
// do something with response
},
function(error) {
// do something with an error
}));
};
Use $q
Change your factory code to this:
angular.module('frontRplApp')
.factory('paymentService', function ($rootScope, $http, config, tools, $q) {
var urlBase = config.baseUrl;
var paymentService = {
response: {},
create: function () {
var deferred = $q.defer();
var args = {};
$http.post(urlBase + 'api/investor/payment/create', args)
.then(function(response){
console.log(response);
paymentService.response = response;
deferred.resolve(response);
}, function (error) {
deferred.reject(error);
});
return deferred.promise;
}
};
return paymentService;
});

Use output from services to controllers

I want to result of my $http.get from my service to my controller.
myserviceSample.js
function messagesService($q,$http){
var messages;
$http({
method: 'GET',
url: 'http://api.com/feedback/list'
})
.then(function success(response){
messages = response.data;
console.log(messages);
},function error(response){
console.log('error'+ response);
});
console.log(messages);
return {
loadAllItems : function() {
return $q.when(messages);
}
};
}
})();
mycontrollerSample.js
function MessagesController(messagesService) {
var vm = this;
vm.messages = [];
messagesService
.loadAllItems()
.then(function(messages) {
console.log(messages);
vm.messages = [].concat(messages);
});
}
})();
The above code results gives undefined output.
What i miss?
$q.when object does expect promise/object to make it working. In your case you have to pass promise object to $q.when as you are doing $http.get call. Here messages object doesn't hold promise of $http.get, so you could change the implementation of method like below.
Service
function messagesService($q,$http){
var messages = $http({
method: 'GET',
url: 'http://api.com/feedback/list'
})
.then(function success(response){
return response.data;
},function error(response){
return $q.reject('Error Occured.');
});
return {
loadAllItems : function() {
return $q.when(messages);
}
};
}
Then controller will resolve that promise & .then will do the trick
function MessagesController(messagesService) {
var vm = this;
vm.messages = [];
messagesService
.loadAllItems()
.then(function(messages) {
console.log(messages);
vm.messages = [].concat(messages);
});
}
Note: Using $q to create a custom promise, is considered as bad pattern when you have $http.get method there(which does return
promise itself)
Improved Implementation
function messagesService($q, $http) {
var messages, getList = function() {
return $http({
method: 'GET',
url: 'http://api.com/feedback/list'
})
.then(function success(response) {
messages = response.data
return response.data;
}, function error(response) {
return $q.reject('Error Occured.');
});
};
return {
loadAllItems: function() {
if (!data)
return getList(); //return promise
else
return $q.resolve(messages); //return data
}
};
};

Undefined when returning $http promise in controller from factory

No matter what I do I always get $$state or undefined back from my factory API call. I've tried promises and simply returning response.data from .then but nothing I tried works.
I can get the proper response data into my controller but then when I try to assign it to anything I just get undefined or $$state, depending on which method I use.
My factory:
factory('forecastFactory', function ($http, $q, SundialConfig) {
var Forecast = {};
var weatherKey = SundialConfig.openWeatherKey;
Forecast.dayCnt = 1;
Forecast.prepareCity = function (city) {
city === undefined ? city = 'Chicago, IL' : city = city;
return city;
}
Forecast.getForecast = function (city) {
var preparedCity = Forecast.prepareCity(city);
var deferred = $q.defer();
$http.jsonp('http://api.openweathermap.org/data/2.5/forecast/daily?', {
params: {
appid: weatherKey,
q: preparedCity,
cnt: Forecast.dayCnt,
callback: 'JSON_CALLBACK'
}
})
.then(function (res) {
console.log("success");
deferred.resolve(res);
})
.catch(function (err) {
console.log('error');
});
return deferred.promise;
}
return Forecast;
});
My controller:
controller('ForecastController', function ($scope, $location, forecastFactory, locationService) {
vm = this;
forecastFactory.getForecast('Chicago, IL').then(function (res) {
console.log(res);
vm.forecast = res;
});
});
I think you don't need to use $q because $http returns a promise,
you can do
Forecast.getForecast = function(city) {
var preparedCity = Forecast.prepareCity(city);
return $http.jsonp('http://api.openweathermap.org/data/2.5/forecast/daily?', {
params: {
appid: weatherKey,
q: preparedCity,
cnt: Forecast.dayCnt,
callback: 'JSON_CALLBACK'
}
})
.then(function(res) {
console.log("success");
return res.data;
})
.catch(function(err) {
console.log('error')
return []; // or {} depending upon required data
});
}
and in controller, do the same as you are doing now
Other way is simply return the promise returned by $http
Forecast.getForecast = function(city) {
var preparedCity = Forecast.prepareCity(city);
return $http.jsonp('http://api.openweathermap.org/data/2.5/forecast/daily?', {
params: {
appid: weatherKey,
q: preparedCity,
cnt: Forecast.dayCnt,
callback: 'JSON_CALLBACK'
}
})
}
and in controller do this
Sundial.Controllers.
controller('ForecastController', ['$scope', '$location', 'forecastFactory', 'locationService', function($scope, $location, forecastFactory, locationService) {
vm = this;
forecastFactory.getForecast('Chicago, IL').then(function(res) {
console.log(res)
vm.forecast = res.data;
}, function(err){
// do something
})
}]);

Returning promise inside async function

I have an AngularJS service method that should return a promise. Within that method is an async call which proceed to do an $http call on success. I am not quite getting the syntax right to return this correctly.
// This function should return a promise
function doFacebook() {
var deferred = $q.defer();
Facebook.login(function(response) {
var p = $http({
method: 'POST',
url: '/auth/facebook',
data: {
token: response.authResponse.accessToken
}
});
p.then(function(data) {
startSession(data);
return data;
}, function(data) {
return $q.reject(data);
});
});
return deferred.promise;
}
I am wanting to return p - not sure how to either attach/replace deferred with it.
You could just resolve deferred object with the promise p returned by the http call inside the facebook login callback. it should resolve/reject the passed-in promise automatically.
function doFacebook() {
var deferred = $q.defer();
Facebook.login(function(response) {
var p = $http({
method: 'POST',
url: '/auth/facebook',
data: {
token: response.authResponse.accessToken
}
}).then(function(data) {
startSession(data);
return data;
}, function(data) {
return $q.reject(data);
});
//Here
deferred.resolve(p);
});
return deferred.promise;
}
A sample demo:-
///BAD CODE ALERT!!
angular.module('app', []).controller('ctrl', function($scope, $q, $http, $timeout) {
function doAsync() {
var deferred = $q.defer();
//Simulating non angular async
setTimeout(function() {
//Simulating http call. $timeout also returns a promise, so.
var p = $timeout(function() {
//uncomment to test reject scenario
if ($scope.reject) {
$scope.reject = false;
throw Error("OOPS i got rejected");
}
return 'Hello, i got resolved!!'
}).then(function(data) {
return data;
}, function(data) {
return $q.reject(data.message);
});
//Here
deferred.resolve(p);
});
return deferred.promise;
}
$scope.runTest = function() {
doAsync().then(function(data) {
$scope.data = data;
}).catch(function(error) {
$scope.data = error;
});
}
$scope.rejectTest = function() {
$scope.reject = true;
}
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<button ng-click="rejectTest()">Reject and then</button>
<button ng-click="runTest()">Run Test</button>
<div>{{data}}</div>
</div>

AngularJS - handling errors with promises [duplicate]

This question already has answers here:
How do I handle errors with promises?
(2 answers)
Closed 8 years ago.
I am using a provider to fetch some data via an API in my angular app and then use it in a controller. The API make call to is sometimes down which results in a 500 error. This error gets printed to the console and I don't know how to handle it gracefully.
Here is my provider code:
function myPvdr() {
this.getUrl = function() {
return 'http://path/to/my/API';
};
this.$get = function($q, $http) {
var self = this;
return {
getData: function(points) {
var d = $q.defer();
$http({
method: 'GET',
url: self.getUrl(),
cache: true
}).success(function(data) {
d.resolve(data);
}).error(function(err) {
d.reject(err);
});
return d.promise;
}
}
}
}
And here is how I use it in my controller:
function myCtrl($scope, myProvider, localStorageService) {
$scope.myData = localStorageService.get('data') || {};
myProvider.getData()
.then(function(data) {
localStorageService.set('data', data);
$scope.data = data;
});
}
How can I handle the 500 error properly, i.e. not throw any error to the console and use the data provided in local storage if any?
Many thanks
You can catch the reject of promise like this :
myProvider.getData()
.then(function(data) {
// promise resolved, data treatment
}, function(error) {
// promise rejected, display error message
});
or
myProvider.getData()
.then(function(data) {
// promise resolved, data treatment
})
.catch(function(error) {
// promise rejected, display error message
});
var app = angular.module('app', []);
function myProvider($http, $q) {
this.getUrl = function() {
return 'http://path/to/my/API';
};
this.getdata = function(points) {
var d = $q.defer();
$http({
method: 'GET',
url: this.getUrl(),
cache: true
}).then(function(data) {
d.resolve(data);
},function(err) {
d.reject(err);
});
return d.promise;
};
return this;
}
app.factory('myProvider', myProvider);
app.controller('firstCtrl', function($scope,myProvider){
// $scope.myData = localStorageService.get('data') || {};
getdata = function() {
myProvider.getdata()
.then(function(data) {
localStorageService.set('data', data);
$scope.data = data;
},
//handle error
function(e){
alert("Error " + e.status);
});
};
getdata();
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="app">
<div ng-controller="firstCtrl">
</div>
</body>

Categories

Resources