I am still trying to learn Jasmine and test an Angular service. Currently I am trying to test the .success and error calls off http.get.
Service Call
this.get = function (param1, param2) {
return $http.get('api/something/get/param1/param2')
.success(function (data) {
return data;
})
.error(function() {
return "Please select param1 AND param2";
});
};
Jasmine Tests
it('service makes unsuccessful API call', function() {
var response = "This is the response";
httpBackend.when('GET', "api/something/Get/0/0").respond(404);
var data;
service.get(0, 0).then(function(result) {
data = result;
});
httpBackend.flush();
expect(data).toEqual("Please select param1 AND param2");
});
it('service makes successful API call', function () {
var response = "This is the response";
httpBackend.when('GET', "api/something/Get/0/0").respond(response);
var data;
service.get(0, 0).then(function(result) {
data = result.data;
});
httpBackend.flush();
expect(data).toEqual(response);
});
In the first test (Error) the data = result.data line in then() is never called. On the expect(data).toEqual(), data is undefined. When I step through everything I see where the service is called and the error message is populated in result.data.
In the second test (Success), I see the same thing but the data is set when the then function is called.
Why is my then function not called on .error()?
success() and error() don't work the same way as then(). The value returned by the callback is ignored. success() and error() return the promise on which they're called, and not a new promise like then().
So, their usage should be limited to callbacks having side-effects only (like initializing a scope variable).
Related
I'm a bit new to $q angular promises. Here is my code structure:
$q.all(promises).then(function(results) {
results.forEach(function(data, status, headers, config) {
console.log(status);
});
$scope.saveDocumentCaseState();
}, function errorCallBack(response) {
console.log('error while mass update case entries');
console.log(response);
$scope.submitToWsSuccessful = 2;
});
Is there a possibility to access the result of each promise when it has been executed before the next one?
So assuming that you receive an array with an arbitrary number of promises from an angular factory called promiseFactory:
var promises = promiseFactory.getPromises(); // Returns an array of promises
promises.forEach(function(p){
p.then(promiseSuccess, promiseError);
function promiseSuccess(){
// Do something when promise succeeds
}
function promiseError(){
// Do something when promise errors
}
});
$q.all(promises).then(allSuccess, allError);
function allSuccess(){
// All calls executed successfully
}
function allError(){
// At least one of the calls failed
}
I work on my angularjs project.
I created this service:
(function () {
"use strict";
angular.module("manageItems").factory("manageItemsService", ["$http", "config", manageItemsService]);
function manageItemsService($http, config) {
var service = {
getNewItems: getNewItems,
};
return service;
function getNewItems(session, mapName) {
return $http.get(serviceUrl + 'getNewItems/' + session + "/" + mapName);
}
}
})();
And here how I call the service from controller:
function getNewItems() {
manageItemsService.getNewItems(mapguideService.mapName, mapguideService.sessionId).then(function (result) {
self.currentItems = result.data;
})
}
I need to make service to delay while the response returned.
How can I change servicefunction to make it wait until self.currentItems property is populated by data?
First I need say that http requests are actually performed asynchronously so as not to stop the application while the result is returned.
So you have two options, use the angular pattern to adjust your method in order to treat the result, so you must pass a callback function to the service, so that the service, not the controller make the association. It would be something like:
Service:
(function () {
"use strict";
angular.module("manageItems").factory("manageItemsService", ["$http", "config", manageItemsService]);
function manageItemsService($http, config) {
var service = {
getNewItems: getNewItems,
};
return service;
function getNewItems(session, mapName, callback, errorCallback) {
$http.get(serviceUrl + 'getNewItems/' + session + "/" + mapName).then(callback, errorCallback);;
}
}
})();
Controller:
function getNewItems() {
manageItemsService.getNewItems(mapguideService.mapName, mapguideService.sessionId, function (result) {
//this callback will be called asynchronously when the response is available
self.currentItems = result.data;
}, function(error) {
// called asynchronously if an error occurs
// or server returns response with an error status.
})
}
The second option is to totally not recommended, inserting a loop while the result is expected... (to bad)
I hope I have helped!
Then you could be put up .then on getNewItems $http call. And based on retrieved response data, decide whether to return data or call another service method.
function anotherFunction(){
return $http.get(url);
}
function getNewItems(session, mapName) {
return $http.get(serviceUrl + 'getNewItems/' + session + "/" + mapName).then(function successCallback(response){
var data = response.data;
//call another function if data is empty
if(!data.length)
return anotherFunction(); //make sure another function should return promise
return data;
});
}
What the code needs to do is chain promises.
To make the getNewItems function chainable, return the derived promise:
function getNewItems() {
//vvvv RETURN promise
return manageItemsService.getNewItems(mapguideService.mapName, mapguideService.sessionId)
.then(function (response) {
self.currentItems = response.data;
//RETURN value to chain
return response.data;
});
};
Then use the returned promise to chain more operations:
getNewItems().then( function(currentItems) {
//Evaluate current Items
if ( ok ) {
return "DONE";
} else {
//RETURN to chain something else
return getOtherItems();
};
}).then( function(otherItems) {
if (otherItems == "DONE") return;
//ELSE
self.otherItems = otherItems;
//Do further chaining
});
Because calling the .then method of a promise returns a new derived promise, it is easily possible to create a chain of promises.
It is possible to create chains of any length and since a promise can be resolved with another promise (which will defer its resolution further), it is possible to pause/defer resolution of the promises at any point in the chain. This makes it possible to implement powerful APIs.
— AngularJS $q Service API Reference - Chaining Promises
I'm trying to get a value from a backend API into my controller using $q.when in angular. The function on my controller is calling a non-async function in a service that is invoking other functions to call the backend with the $http service.
$q.when will successfully log the response object but when I try to log the properties of the object, they come up blank. What do I need to do with this? I guess the values are being assigned before the response comes back but I thought the .then statement in $q when was supposed to take care of this?
Controller:
init();
function init(){
$q.when(MyProfileService.fetchProfile()).then(function(response){
// This logs the object and I can see the email property is populated:
console.log("resolved with value:");
console.log(response);
// This comes up with blank value
console.log("attempting to log response data:")
console.log(response.user.email);
});
}
MyProfileService:
function fetchProfile() {
return ProfileService.getProfile()
.then(function(response){
var profileObject = ProfileSaveService.getData();
var transRef = { transientRef: profileObject.card.transientRef }
// and pass it into the function for retrieving the estatement optin details.
fetchEstatementStatus(transRef);
return service;
}).catch(function(error){
console.error(error);
});
}
function fetchEstatementStatus(transRef) {
return HttpService.httpPost('/dash/v1/account/retrieveCommProfile', transRef)
.then(function(response) {
service.user.email = response.data.userEmail;
// This should return the service object with all the properties set:
return service;
})
.catch(function(error) {
return $q.reject(error);
});
}
You fire off the asynchronous fetchEstatementStatus(transRef); call but never wait for it. Instead, you immediately return the service. You need to return the promise from the then callback to make it wait:
function fetchProfile() {
return ProfileService.getProfile().then(function(response){
var profileObject = ProfileSaveService.getData();
return fetchEstatementStatus({transientRef: profileObject.card.transientRef});
// ^^^^^^
}).catch(function(error){
console.error(error);
});
}
function fetchEstatementStatus(transRef) {
return HttpService.httpPost('/dash/v1/account/retrieveCommProfile', transRef).then(function(response) {
service.user.email = response.data.userEmail;
return service;
});
}
I have this code
PedidosService.getProductbyID($scope.listProductos.ProductID).then(function (d) {
$scope.oneProduct = d.data.producto;
});
PedidosService is the name of my factory, getProductbyId is my http get request and $scope.oneProduct is the variable where i want store the result of this request.
factory.getProductbyID = function (id) {
return $http.get('/Pedidos/GetProduct/' + id);
}
factory is my Factory and getProductbyID is my function to call http request
I call this code in a button. The first time that I click in the button, it returns a empty response '[]' but the next times that I click the button, it works fine!!!.
Thanks for you help
As far as I know the $http.get returns a promise because that call is asynchronous. With that in mind a typical call to $http.get should be something like this:
$http.get('/someUrl', config).then(successCallback, errorCallback);
In your context I would have done it this way, so the factory returns a promise and in your controllers you will handle the success and error callback appropriately:
factory.getProductbyID = function (id) {
var deffered = $q.defer();
$http.get('/Pedidos/GetProduct/'+ id)
.then(function (result) {
deffered.resolve(result);
}, function (data) {
deffered.reject(data);
});
return deffered.promise;
}
I think I may be missing something fundamental about how a promise works, because I never seem to be getting the result I expect, so hoping someone can correct my thinking.
In this case, it is a relatively easy call. I have a angular factory that among other things gets some data from the API (which makes a call to mongodb).
As there is a $http request (which is asynchronous), I wrapped the http call in a $q function which should return a resolve if successful and a reject if an error.
factory.loadLayout = function (layoutName) {
return $q(function(resolve, reject){
$http.get('/api/getlayout/'+layoutName)
.success(function (data) {
layout = data;
console.log("Got " + layout.name);
resolve('OK');
})
.error(function(data,status,headers,config){
reject(new Error( status));
});
});
}
I then have another function which is dependent on the data collected in the first function, called getButtonId, but as far as I can tell, and even though it is wrapped in the .then, it seems like it is called before the promise is resolved.
var promise = padArea.loadLayout(layoutName);
promise.then(padArea.getButtonId('A0'));
So what am I missing?
== UPDATE ==
So trying the same thing using q.defer
factory.loadLayout = function (layoutName) {
var defer = $q.defer()
$http.get('/api/getlayout/'+layoutName)
.success(function (data) {
layout = data;
console.log("Got " + layout.name);
defer.resolve('OK');
})
.error(function(data,status,headers,config){
defer.reject(new Error( status));
});
return defer.promise;
}
Still not working as I expect, and the function inside .then is still called before http have completed.
== UPDATE 2 ==
OK, so got it working (if I just call the function in the factory inside the .then, it calls it directly, however, if I wrap it in a function as below, it all of a sudden works. Can anyone explain why though, because to me it seems like wrapping the call to a function inside a function should make no difference from just calling the function.
padArea.loadLayout(layoutName).then(function(result){
padArea.getButtonId('A0')
});
Strangely enough, as long as I wrap the second call in ( the one inside the .then ) my original code works as well.
Just use $http promise:
factory.loadLayout = function (layoutName) {
return $http.get('/api/getlayout/'+layoutName)
.success(function (data) {
layout = data;
console.log("Got " + layout.name);
resolve('OK');
})
.error(function(data,status,headers,config){
reject(new Error( status));
});
}
And then to use it...
factory.loadLayout(param).then(function (response) { ... });
OR
I see what you are trying to do. Instead create a deffered from $q
factory.loadLayout = function (layoutName) {
var defer = $q.defer();
$http.get('/api/getlayout/'+layoutName)
.success(function (data) {
layout = data;
console.log("Got " + layout.name);
defer.resolve('OK');
})
.error(function(data,status,headers,config){
defer.reject(new Error( status));
});
return defer.promise;
}
You could also use $q.defer():
deferred = $q.defer();
$http.get('/api/getlayout/'+layoutName)
.success(function (data) {
layout = data;
deferred.resolve("OK");
})
.error(function (data,status,headers,config){
deferred.reject(new Error(status));
})
return deferred.promise;
This can be a little more general if you're not just doing an http request, and lets you control the return.