How to handle $http errors when using factories in angular - angularjs

I have a controller and factory like below and can easily handle success..But how can I handle errors?
Controller
app.controller("draftsCtrl", ["$scope", "DashboardFactory", function ($scope, DashboardFactory) {
DashboardFactory.drafts(function (successCallback) {
$scope.rooms listings= successCallback;
});
}]);
Factory
app.factory('DashboardFactory', function ($http) {
var DashboardFactory = {};
DashboardFactory.active_listings = function (successCallback) {
$http.get('active.json').success(successCallback);
}
DashboardFactory.inactive_listings = function (successCallback) {
$http.get('inactive.json').success(successCallback);
}
DashboardFactory.drafts = function (successCallback) {
$http.get('drafts.json').success(successCallback);
}
return DashboardFactory;
});

Instead of passing callbacks around, prefer proper promises workflow. For this make your service methods return promise objects:
app.factory('DashboardFactory', function ($http) {
var DashboardFactory = {};
DashboardFactory.active_listings = function () {
return $http.get('active.json');
}
DashboardFactory.inactive_listings = function () {
return $http.get('inactive.json');
}
DashboardFactory.drafts = function () {
return $http.get('drafts.json');
}
return DashboardFactory;
});
Then use promise API to handle success (then callback) and errors (catch):
app.controller("draftsCtrl", ["$scope", "DashboardFactory", function ($scope, DashboardFactory) {
DashboardFactory.drafts().then(function (response) {
$scope.rooms_listings = response.data;
})
.catch(function() {
console.log('Error ocurred');
});
}]);

"service" looks more elegantly in this case
function DashboardFactory($http) {
this.active_listings = function () {
return $http.get('active.json');
};
this.inactive_listings = function () {
return $http.get('inactive.json');
};
this.drafts = function () {
return $http.get('drafts.json');
};
});
DashboardFactory.$inject = ['$http'];
app.factory('DashboardFactory', DashboardFactory);

Related

TypeError: Cannot read property 'then' of undefined at m.$scope.profile

I am trying to call a child service function in Main Controller.
But I am getting error on than in profile function.
This is my profile function inside main controller.
App.controller("globalCtrl", function($scope,$rootScope, $window, $location, $rootScope, $cookieStore, toastr, ClientService,) {
var theClient = $cookieStore.get('UserData') || {};
$scope.profile = function(theClient) {
var getCurrentClient = theClient;
var getOrganizationId = $rootScope.globalSession.OrganizationId;
if($rootScope.globalSession.UserRole == "Admin")
{
if (getOrganizationId) {
ClientService.getClient(getOrganizationId).then(function(aGetClientResponse) {
if(getClientResponse[0] == $scope.RESPONSE_CODE.CM_SUCCESS) {
$scope.myprofile = getClientResponse[1];
$location.path("/profile");
}
else {
toastr.warning($scope.USER_MESSAGE.SERVICE_NOT_AVAILABLE, '');
}
});
}
}
};
)};
This is Service of another controller whom function "getClient" I am calling in profile function.
App.factory('ClientService', function($http, $cookieStore, uuid2, API_URL, REQUEST_HEADER, RESPONSE_CODE) {
var theClient = $cookieStore.get('UserData') || {};
return {
getClient: function() {
if(theClient.OrganizationId) {
//API Call
var promise = $http.get(API_URL.GET_CLIENT+theClient.OrganizationId+"&CorrelationId="+uuid2.newuuid()+"&ContextOrganizationId=009", REQUEST_HEADER).then(
function(aGetClientResponse) { //Success Callback
return [aGetClientResponse.data.GetClientResponse.Result.ResponseCode, aGetClientResponse.data.GetClientResponse];
},
function(aGetClientResponse) { //Error Callback
return [aGetClientResponse.status,''];
});
}
return promise;
},
setClient: function(aClient) {
theClient = aClient;
$cookieStore.put('UserData', theClient);
}
}
});
Need Help. cant figure out the Problem. Thanks in advance!
Change your service to just return a promise and handle that promise in your controller using then
The service directly returns API call, no then in service
var promise = $http.get(API_URL.GET_CLIENT+theClient.OrganizationId+"&CorrelationId="+uuid2.newuuid()+"&ContextOrganizationId=009", REQUEST_HEADER)
Here is your service:
App.factory('ClientService', function($http, $cookieStore, uuid2, API_URL, REQUEST_HEADER, RESPONSE_CODE, $q) {
var theClient = $cookieStore.get('UserData') || {};
return {
getClient: function() {
if(theClient.OrganizationId) {
var promise = $http.get(API_URL.GET_CLIENT+theClient.OrganizationId+"&CorrelationId="+uuid2.newuuid()+"&ContextOrganizationId=009", REQUEST_HEADER)
}
else {
var promise = $q.reject();
}
return promise;
},
setClient: function(aClient) {
theClient = aClient;
$cookieStore.put('UserData', theClient);
}
}
});
Now, the controller handles the success and failure response using then
ClientService.getClient(getOrganizationId).then(function(){},function(){})
Here is your controller, observe then
App.controller("globalCtrl", function($scope,$rootScope, $window, $location, $cookieStore, toastr, ClientService) {
var theClient = $cookieStore.get('UserData') || {};
$scope.profile = function(theClient) {
var getCurrentClient = theClient;
var getOrganizationId = $rootScope.globalSession.OrganizationId;
if($rootScope.globalSession.UserRole == "Admin")
{
if (getOrganizationId) {
ClientService.getClient(getOrganizationId).then(function(aGetClientResponse) {
var response = [aGetClientResponse.data.GetClientResponse.Result.ResponseCode, aGetClientResponse.data.GetClientResponse];
if(response[0] == $scope.RESPONSE_CODE.CM_SUCCESS) {
$scope.myprofile = response[1];
$location.path("/profile");
}
else {
toastr.warning($scope.USER_MESSAGE.SERVICE_NOT_AVAILABLE, '');
}
},
function(aGetClientResponse) { //Error Callback
return [aGetClientResponse.status,''];
});
}
}
};
)};
In getClient() the code looks like this:
if(theClient.OrganizationId) {
//API Call
var promise = ... some stuff ...;
}
return promise;
So if the condition is false you return undefined which doesn't have a .then property.
You should make sure that a function which returns a promise always returns a promise. If you want to signal that a missing id is an error then just return $q.reject(something) for an already rejected promise.
I'm not sure it this works, but instead of returning a object with the functions inside, have you tried to declare them as part of the factory object? I have some declared like this and they are working, so have a try:
App.factory('ClientService', function($http, $cookieStore, uuid2, API_URL, REQUEST_HEADER, RESPONSE_CODE) {
var theClient = $cookieStore.get('UserData') || {};
var function = {
getClient: function() {
if(theClient.OrganizationId) {
//API Call
var promise = $http.get(API_URL.GET_CLIENT+theClient.OrganizationId+"&CorrelationId="+uuid2.newuuid()+"&ContextOrganizationId=009", REQUEST_HEADER).then(
function(aGetClientResponse) { //Success Callback
return [aGetClientResponse.data.GetClientResponse.Result.ResponseCode, aGetClientResponse.data.GetClientResponse];
},
function(aGetClientResponse) { //Error Callback
return [aGetClientResponse.status,''];
});
}
return promise;
},
setClient: function(aClient) {
theClient = aClient;
$cookieStore.put('UserData', theClient);
}
}
return function;
});
You can have a try with this too:
Define the services in the controller declaration (and remove one rootScope, its duplicated):
App.controller("globalCtrl", ['$scope','$rootScope','$window','$location','$cookieStore','toastr','ClientService', function($scope,$rootScope, $window, $location, $cookieStore, toastr, ClientService){
[...]
}]);
Try something like this to ensure that there is always a promise object available.
getClient: function() {
var deferred = $q.defer();
if(theClient.OrganizationId) {
//API Call
$http.get(API_URL.GET_CLIENT+theClient.OrganizationId+"&CorrelationId="+uuid2.newuuid()+"&ContextOrganizationId=009", REQUEST_HEADER).then(
function(aGetClientResponse) { //Success Callback
deferred.resolve(aGetClientResponse);
},
function(aGetClientResponse) { //Error Callback
deferred.resolve(aGetClientResponse);
});
}else{
deferred.reject('Missing OrganizationId');
}
return deferred.promise;
}
In the else part you can return dummy promise with either resolve($q.resolve()) or reject($q.reject()) status.
JS :
getClient: function() {
if(theClient.OrganizationId) {
//API Call
var promise = $http.get(API_URL.GET_CLIENT+theClient.OrganizationId+"&CorrelationId="+uuid2.newuuid()+"&ContextOrganizationId=009", REQUEST_HEADER).then(
///your rest code
return promise;
}else{
return $q.reject();
}
}
With $q.reject() it will call error handler in profile call.So,add error handler callback as second parameter
ClientService.getClient(getOrganizationId).then(function(aGetClientResponse) {
//success code
},
function(){ //add it in your script
//check error here
});

Use http response outside?

I have a problem when trying to call a variable outside a function
This is the service
app.factory('Service', function($http) {
return {
agentes: function () {
return $http.get(base_url +'/api/auth/organization/me/tasks/calendar').then(function (response) {
return response.data;
});
}
};});
within the controller I call:
loadAgents();
function loadAgents() {
Service.agentes()
.then(function(agentes){
$scope.agentes = agentes.data.trackables;
});
}
within the above function I can use $scope.agentes but not outside it ...
I dont understand exactly what is your problem but you have some "mistakes" in your code.You should do
app.factory('Service', function($http) {
return {
agentes: function () {
return $http.get(base_url +'/api/auth/organization/me/tasks/calendar');
}
}
});
Then inside your controller
app.controller('myController', ['Service', function(Service) {
function loadAgents() {
Service.agentes()
.then(function(agentes){
$scope.agentes = agentes.data.trackables;
});
}
}])

How to test Angular controller having a service

I am having a controller and service like below
(function () {
var mockController = function ($scope, MockService) {
$scope.message = "This is a text message";
$scope.getCities = function () {
$scope.places = [];
MockService.getCities().then(function (response) {
var places = response.data["weather-app:root"].city;
if (places) {
if (Array.isArray(places)) {
$scope.places = places;
} else {
$scope.places.push(places);
}
}
});
};
};
var mockService = function ($http) {
this.getCities = function () {
return $http.get("../rest/url", {
headers: {
'Accept': 'application/yang.data+json'
}
});
};
};
angular.module("MockApp", [])
.service("MockService", mockService)
.controller("MockController", mockController);
}())
I created a mock service like below for mocking the service for unit testing.
(function () {
angular.module('mock.service', [])
.service('MockService', function ($q) {
var mockService = {};
mockService.getCities = function () {
var mydata = {
"weather-app:root": {
"city": [
{
"city-name": "Chennai"
, "country-name": "India"
}
, {
"city-name": "Mangalore"
, "country-name": "India"
}
]
}
}
return $q.when(mydata);
};
return mockService;
});
}());
My test case is like
describe("MockController", function () {
var $scope;
beforeEach(function () {
module("MockApp");
beforeEach(module('mock.service'));
inject(function (_$controller_, _$rootScope_, _MockService_) {
$scope = _$rootScope_.$new();
controller = _$controller_("MockController", {
$scope: $scope
, MockService: _MockService_
});
});
});
describe("Test", function () {
it("Should be Bangalore", function () {
$scope.getCities();
console.log($scope.places);
});
});
});
the problem is that the then method in controller is not getting called. How can I resolve the issue ?
Three things to fix...
Don't nest the beforeEach calls. You can init multiple modules with module.
beforeEach(function() {
module('MockApp', 'mock.service');
// and so on
Your mock data does not quite match what you'd see from an $http based promise response
return $q.when({data: mydata});
In order to process promises, you need to trigger a digest cycle
it("Should be Bangalore", function() {
$scope.getCities();
$scope.$apply();
console.log($scope.places);
});

Not able to invoke the API Services when something is between Controller and Service in AngularJS?

I have Product.js file where
var productOPS = {
GetProduct: function () {
var promiseGet = getProducts();
}};
Now I want to invoke ProductService.js
app.service('crudService', function ($http) {
///product GET
this.getProducts = function () {
return $http.get("/api/ProductsAPI");
}
///end product
});
And ProductController.js is
app.controller('crudController', function ($scope, crudService) {
GetProducts();
//Function to load all Employee records
function GetProducts()
{
productOPS.GetProduct();
} } });
How can I invoke the Service from GetProduct of Products.js and pass the value back to ProductController
Error: getProducts() is not defined.
service should return object
app.service('crudService', function ($http) {
return {
getProducts: function () {
return $http.get("/api/ProductsAPI");
}
});
and then in controller you can sue it like this:
app.controller('crudController', function ($scope, crudService) {
crudService.getProducts().then( function(response){
$scope.products = response.data
});
});
$http returns promise that's why I used then but you can do success or whatever you want

How to get data by service and $cacheFactory by one method

I have a factory which get data from server. In the factory method I have used $cacheFactory to cache getting data. My code is as follows..
var buyersService = function ($http, $q,$cacheFactory) {
var serviceBase = '/api/OMData/';
var BuyersFactory = {};
buyersService.cache = $cacheFactory('cacheId');
BuyersFactory.GetBuyers = function () {
var dataList = buyersService.cache.get('BuyerData');
if (dataList != null && dataList.length > 0) {
return dataList;
}
else {
return $http.get(serviceBase + 'GetBuyers').then(
function (results) {
buyersService.cache.put("BuyerData", results.data);
return results.data;
});
}
}
app.factory('OMDataService', ['$http', '$q', '$cacheFactory', buyersService]);
});
Now I have called GetBuyers method from controller. My method is like below..
var BuyerController = function ($scope, BuyersService) {
$scope.Buyers = [];
init();
function init() {
getBuyers();
}
function getBuyers() {
BuyersService.GetBuyers()
.then(function (data) {
$scope.Buyers = data;
}, function (error) {
alert(error.message);
});
}
};
app.register.controller('BuyersController', ['$scope', 'OMDataService', BuyerController]);
When I have executed my controller method second time I have got an error message in promise part.
Object doesn't support property or method 'then'
The issue here is that your function returns two different things: either a promise or plain data. To remedy this, use another promise to control the flow and return that one as the result of the function.
Update your code to
var buyersService = function ($http, $q,$cacheFactory) {
var serviceBase = '/api/OMData/';
var BuyersFactory = {};
buyersService.cache = $cacheFactory('cacheId');
BuyersFactory.GetBuyers = function () {
var buyersDataIsAvailable = $q.defer();
var dataList = buyersService.cache.get('BuyerData');
if (dataList != null && dataList.length > 0) {
buyersDataIsAvailable.resolve(dataList);
}
else {
$http.get(serviceBase + 'GetBuyers').then(
function (results) {
buyersService.cache.put("BuyerData", results.data);
buyersDataIsAvailable.resolve(results.data);
});
}
return buyersDataIsAvailable.promise;
}
app.factory('OMDataService', ['$http', '$q', '$cacheFactory', buyersService]);
});

Resources