This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 6 years ago.
I have the following scenario, I need data from a particular url.I also need to save this datas into a variable that I can call everywhere in my controller. To overcome such issue, I have written a service like below :
app.service("userData", function ($http, $q) {
var deferred = $q.defer();
this.getUsers = function () {
return $http.get('/users')
.then(function (response) {
deferred.resolve(response.data);
return deferred.promise;
}, function (response) {
deferred.reject(response);
return deferred.promise;
})
;
};
});
And then my controller looks like :
app.controller('UserCtrl', function($scope, $q, userData) {
var myData = userData.getUsers()
.then(
function (result) {
$scope.users = result;
},
function (error) {
console.log(error.statusText);
}
);
console.log(myData)
});
It prints :
So how can i store data into a variable which is accessible all over the controller ?
Thanks..
I suggest you something like that:
angular.module("app").factory("userService", userService);
userService.$inject = ["$http", "$q"];
function userService($http, $q) {
var deferred = $q.defer();
var promise = null;
var currentUser = null;
return {
getUserInfo: function () {
if (promise) {
deferred.resolve(currentUser);
return (currentUser) ? deferred.promise : promise;
}
else {
promise = $http.get("http://swapi.co/api/people/1/").then(function (response) {
return currentUser = response.data.name;
});
return promise;
}
}
}
}
you can call then any time userService.getUserInfo() to get the cached data.
.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/view1', {
templateUrl: 'view1/view1.html',
controller: 'View1Ctrl',
controllerAs: 'vc'
});
}])
.controller('View1Ctrl', function (userService) {
var vm = this;
userService.getUserInfo().then(function (result) {
vm.name = result
}, function (err) {
vm.name = err
});
});
I create a git repository to test it out: https://github.com/leader80/angular-cache-service
I hope it helps
Related
I am trying to pass the data between controllers when each http request is successful. that said, here is a hierarchy
<div ng-controller="ParentCtrl as parent">
<div ng-controller="ChildOneCtrl as chi1"></div>
<div ng-controller="ChildTwoCtrl as chi2"></div>
<div ng-controller="ChildThreeCtrl as chi3"></div>
</div>
loading data within each next controller depends on previous one, namely when http request is successful.
ChildOneCtrl:
function ChildOneCtrl ($scope, sharedService) {
var chi1 = this;
sharedService.getChildOneData()
.then(function (res) {
$rootScope.$emit('childOneDataEmited', res.data);
}).catch(function (err) {
console.log(err);
});
}
ChildTwoCtrl:
function ChildTwoCtrl ($scope, $rootScope, sharedService) {
var chi2 = this;
var onEmitChildOne = $rootScope.$on('childOneDataEmited', function (event, data) {
getData(data);
});
$scope.$on('$destroy', onEmitChildOne);
function getData(data){
var chi1Data = data;
if(chi1Data.uEnabled){
sharedService.getChildTwoData()
.then(function (res) {
$rootScope.$emit('childTwoDataEmited', res.data);
}).catch(function (err) {
console.log(err);
});
}
}
}
ChildThreeCtrl:
function ChildThreeCtrl ($scope, $rootScope, sharedService) {
var chi3 = this;
var onEmitChildThree = $rootScope.$on('childTwoDataEmited', function (event, data) {
getData(data);
});
$scope.$on('$destroy', onEmitChildThree);
function getData(data){
var chi2Data = data;
sharedService.getChildThreeData()
.then(function (res) {
//to do some data manipulation
console.log(res)
console.log(chi2Data)
}).catch(function (err) {
console.log(err);
});
}
}
while this get the job done, the hierarchy is likely going to change, become deeper so I am wondering if there is a better way to do it so I dont overuse events ?
Why don't you share the data it self through the sharedService ?
After you get the data from first controller, just assign it to a shared variable in sharedService and in the child controller (second one) just set a watch for this shared variable. like this:
function ChildOneCtrl ($scope, sharedService) {
var chi1 = this;
sharedService.getChildOneData()
.then(function (res) {
sharedService.sharedData = res.Data;
}).catch(function (err) {
console.log(err);
});
}
function ChildTwoCtrl ($scope, $rootScope, sharedService) {
var chi2 = this;
$scope.watch('sharedService.sharedData', function(newValue) {
// do something like calling another endpoint using http.
});
}
I didn't try it, it may fail, but the idea is to share data throug service.
UPDATE
Another approach is to have two then() or more:
function sharedService($q, $http) {
var service = {
sharedData1Promise: { then: function() {} },
sharedData2Promise: { then: function() {} },
sharedData3Promise: { then: function() {} },
getSomeData1: getSomeData1
};
function getSomeData1() {
sharedData1Promise = $http.get( /* what ever is here */ );
return sharedData1Promise;
}
function getSomeData2() {
sharedData2Promise = $http.get( /* what ever is here */ );
return sharedData2Promise;
}
function getSomeData3() {
sharedData3Promise = $http.get( /* what ever is here */ );
return sharedData3Promise;
}
return service;
}
function ChildOneCtrl ($scope, sharedService) {
var chi1 = this;
sharedService.getSomeData1()
.then(function (res) {
/* do something */
}).catch(function (err) {
console.log(err);
});
}
function ChildTwoCtrl ($scope, sharedService) {
var chi2 = this;
sharedService.sharedData1Promise
.then(function (res) {
sharedService.getSomeData2();
/* do something with data coming from the first child calling getSomeData1 method */
}).catch(function (err) {
console.log(err);
});
}
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;
});
This question already has answers here:
Is this a "Deferred Antipattern"?
(3 answers)
Closed 5 years ago.
In my $routeProvider I have a resolve property to get data from an API before I instantiate my controller. Why is it when I use the $q with the $http service, I can pass the data to my controller. Aren't they both doing the same thing?
Here is my original approach where the data wasn't able to get passed to the controller:
AccountService.js
app.factory('AccountService', ['$http', '$q', function ($http, $q) {
return {
GetAccounts: function () {
return $http.get('api/Account/GetAccounts')
.then(function success(response) {
return response;
}, function error(response) {
̶r̶e̶t̶u̶r̶n̶ throw console.log("Oops!");
});
},
};
}]);
To pass the data into the controller I change up the GetAccounts to this:
app.factory('AccountService', ['$http', '$q', function ($http, $q) {
return {
GetAccounts: function () {
var deferred = $q.defer();
$http.get('api/Account/GetAccounts')
.then(function (data) {
deferred.resolve(data.data);
})
.catch(function (response) {
deferred.reject(response);
})
return deferred.promise;
},
};
}]);
route.js
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/users', {
template: '<admin accounts="$resolve.accounts"></admin>',
resolve: {
accounts: function (AccountService) {
return AccountService.GetAccounts()
.then(function (data) {
return data;
})
}
}
})
}]);
$q is Angulars wrapper for promises. $http returns a promise directly so whether you create a new promise with a new $q or $http directly, they are the same
So essentially you dont have to do what you do in AccountService, only this is needed:
app.factory('AccountService', ['$http', function ($http) {
return {
GetAccounts: function () {
return $http.get('api/Account/GetAccounts')
.then(function (data) {
return data.data;
})
},
};
}]);
I would save the .catch() for your resolve to handle and maybe wrap the error in a nice error construct unless your backend already does that.
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/users', {
template: '<admin accounts="$resolve.accounts"></admin>',
resolve: {
accounts: function (AccountService) {
return AccountService.GetAccounts()
.catch( function(err) { ̶r̶e̶t̶u̶r̶n̶ throw new BusinessError( err ) } )
}
}
})
}]);
or let the calling controller/component handle it, its an architecture decision.
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
})
}]);
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>