How to store data from http service in angular factory - angularjs

I would like to store the value from /api/login.json globally using a service, but I think I have some sort of timing issue. The console.log statement in the controller tells me that the scope.login object is undefined.
What am I missing?
Thanks!
Factory service:
myApp.factory('LoginFactory', ['$http', function($http){
this.data;
$http.get('/api/login.json').success(function(data) {
this.data = data;
});
return {
getData : function(){
return this.data;
}
}
}]);
Controller:
myApp.controller('AccountsCtrl', ['$scope', 'Accounts', 'LoginFactory', function($scope, Accounts, LoginFactory){
$scope.login = LoginFactory.getData();
console.log('$scope.login: %o', $scope.login);
$scope.accounts = Accounts.index();
}]);

you should probably avoid use of the this keyword in this context. better just to declare a new variable.
myApp.factory('LoginFactory', ['$http', function ($http) {
var data;
$http.get('/api/login.json').success(function (d) {
data = d;
});
return {
getData: function () {
return data;
}
};
}]);
you will still have a race issue though, so i would also recommend either promise chaining
myApp.factory('LoginFactory', ['$http', function ($http) {
var promise = $http.get('/api/login.json');
return {
getData: function (callback) {
promise.success(callback);
}
};
}]);
or even a conditional get
myApp.factory('LoginFactory', ['$http', function ($http) {
var data;
return {
getData: function (callback) {
if(data) {
callback(data);
} else {
$http.get('/api/login.json').success(function(d) {
callback(data = d);
});
}
}
};
}]);
The last two approaches require you to rewrite your controller though
myApp.controller('AccountsCtrl', ['$scope', 'Accounts', 'LoginFactory', function($scope, Accounts, LoginFactory){
LoginFactory.getData(function(data) {
$scope.login = data;
console.log('$scope.login: %o', $scope.login);
$scope.accounts = Accounts.index(); //this might have to go here idk
});
}]);

Extending #LoganMurphy answer. Using promise and still adding callbacks is not at all desirable. A better way of writing service could be
myApp.factory('LoginFactory', ['$http', function ($http, $q) {
var data;
return {
getData: function () {
if(data) {
return $q.when(data);
} else {
return $http.get('/api/login.json').then(function(response){
data = response;
return data;
});
}
}
};
}]);

You have an issue with the this keyword and also you not handling the promise from the http.get correctly
I would write it like this:
myApp.factory('LoginFactory', ['$http', function($http){
return {
getData : function(){
return $http.get('/api/login.json');
}
}
}]);
myApp.controller('AccountsCtrl', ['$scope', 'Accounts', 'LoginFactory', function($scope, Accounts, LoginFactory){
$scope.login = LoginFactory.getData().success(function(data){
console.log(data);
console.log('$scope.login: %o', $scope.login);
});
}]);

Related

Angular reduce api calls

I am creating one sample application, where I have API call for getting classes say
http://localhost:8080/school/4/classes
I have created a service for this
appServices.service( 'classService', ['$http', '$q',
function($http,$q){
this.getClass = function() {
var classes = $q.defer()
$http.get( "http://localhost:8080/school/4/classes" )
.then(function(data) {
classes.resolve(data)
});
return classes.promise
}
}])
I have two controllers say ctrl1 and ctrl2, in both I have code for service as
classService.getClass().then(function(data) {
$scope.classList = data.data.classes
})
My problem is two time api call is happening, can we reduced many api calls to one because my data is not going to be changed. I have already tried with { cache: true } but no luck
Thanks
The simplest way to prevent multiple calls is to use cache option:
app.service('classService', ['$http', function($http) {
this.getClass = function() {
return $http.get('data.json', { cache: true }).then(function(response) {
return response.data;
});
};
}])
Note, that you should not use $q as it's redundant.
In case if you need more control over the cache you can store reference to resolved promise:
app.service('classService', ['$http', function($http) {
var promise
this.getClass = function() {
if (!promise) {
promise = $http.get('data.json').then(function(response) {
return response.data;
});
}
return promise
};
}]);
And one more pattern with the most flexibility:
app.service('classService', ['$http', '$q', function($http, $q) {
var data;
this.getClass = function() {
return data ? $q.when(data) : $http.get('data.json').then(function(response) {
data = response.data;
return data;
});
};
}])

Update data from a factory in another factory

I have two factories in my angular app.
app.factory('FavoritesArtists',['$http', '$q', 'UserService', function($http, $q, UserService){
var deferred = $q.defer();
var userId = UserService.getUser().userID;
$http.get('http://myurl.com/something/'+userId)
.success(function(data) {
deferred.resolve(data);
})
.error(function(err) {
deferred.reject(err);
});
return deferred.promise;
}]);
And I have a value I get from another factory :
var userId = UserService.getUser().userID;
But I doesn't update when the UserService.getUser() is changed, the view changes with $watch, but I don't know how it work inside a factory.
Any help is welcome, thanks guys !
Anyone ?
app.factory('FavoritesArtists',['$http', '$q', 'UserService', function($http, $q, UserService){
var deferred = $q.defer();
$http.get('https://homechefhome.fr/rise/favorites-artists.php?user_id='+userId)
.success(function(data) {
deferred.resolve(data);
})
.error(function(err) {
deferred.reject(err);
});
return deferred.promise;
}]);
I simply need to make userId variable dynamic..
A better design pattern will be this
app.factory('UserService',
['$http', function($http) {
var getUser = function() {
return http.get("url_to_get_user");
};
return {
getUser: getUser
};
}]);
app.factory('ArtistService',
['$http', '$q', 'UserService', function($http, $q, UserService) {
var getFavoriteArtists = function() {
UserService.getUser().then(function(response) {
var userId = response.data.userID;
return $http.get('http://myurl.com/something/' + userId);
}, function() {
//passing a promise for error too
return $q.reject("Error fetching user");
});
};
return {
getFavoriteArtists: getFavoriteArtists
};
}]);
Now call it in controller like,
ArtistService.getFavoriteArtists().then(function(response) {
//do something with response.data
}, function(response) {
//log error and alert the end user
});

Promise returning object in angular js

i have my authservice as given below ,
myApp.factory('Authentication',
['$rootScope', '$location', 'URL', '$http', '$q',
function ($rootScope, $location, URL, $http, $q) {
var myObject = {
authwithpwd: function (user) {
var dfd = $q.defer();
$http
.post('Mart2/users/login', {email: user.email, password: user.password})
.then(function (res) {
return dfd.resolve(res.data);
}, function (err) {
return dfd.reject(err.data);
});
return dfd.promise;
} //login
};
return myObject;
}]); //factory
And i'm using that service in user service as follows :
myApp.factory('UserService',
['$rootScope', '$location', 'URL', '$http', '$q', 'Authentication',
function ($rootScope, $location, URL, $http, $q, $Authentication) {
var myObject = {
login: function (user) {
$Authentication.authwithpwd(user).then(function (regUser) {
console.log(regUser);
}).catch(function (error) {
$rootScope.message = error.message;
});
},
getUserToken: function () {
return $rootScope.currentUser.apiKey;
},
isLogged: function () {
if ($rootScope.currentUser) {
return true;
} else {
return false;
}
}
//login
};
return myObject;
}]); //factory
I am very new to angular js . While writing service and calling that service from controller i have put a console debug in user service which is showing its returning object .i am getting object if i do console.log(regUser) ? any idea why ?
To get the object you need to do change your myObject declaration. Basically you need to return a promise from the login function and then write a callback to get the resolved data.
myApp.factory('UserService',
['$rootScope', '$location', 'URL','$http','$q','Authentication',
function($rootScope,$location, URL,$http,$q,$Authentication) {
var myObject = {
login: function(user) {
var defer = $q.defer();
$Authentication.authwithpwd(user).then(function(regUser) {
console.log(regUser);
defer.resolve(regUser);
}).catch(function(error) {
$rootScope.message = error.message;
defer.reject(regUser);
});
return defer.promise;
},
getUserToken:function() {
return $rootScope.currentUser.apiKey;
},
isLogged:function() {
if($rootScope.currentUser){
return true;
} else {
return false;
}
}//login
};
return myObject;
}]); //factory
To extract the object from controller or from some other service you need to write a callback
UserService.login(user)
.then(function (data) {
$scope.data = data;
}, function (error) {
$scope.error = error;
});
Also in the Authentication service you can just do a 'dfd.resolve' instead of 'return dfd.resolve'; since you are already returning the dfd.promise.
I have created a fiddler here

Simple promise with Angular's $http

I find myself doing things like this a lot in my Angular services:
getStats: function() {
var deferred = $q.defer();
$http.get('/stats').success(function(stats) {
deferred.resolve(stats);
});
return deferred.promise;
}
Note that the promise I'm returning resolves to the returned data. It's not a situation where I can use a .success callback.
Is there a simpler way to do this without using $q?
That should work, because $http.get returns promise object,
getStats: function() {
return $http.get('/stats');
}
in your code you use it like this:
someService.getStats().success(successFN);
Create an API service:
app.factory('apiService', ['$http', function($http) {
'use strict';
return {
getStats: function (deviceId) {
return $http.get('/stats');
}
}
}
Then inject the apiCall in your controller and use the getStats method.
app.controller("MyController", ['$scope', 'apiService', '$routeParams', function($scope, apiService, $routeParams) {
'use strict';
angular.extend($scope, {
info: {},
deviceId: $routeParams.deviceId,
loadInfo: function () {
apiService
.getStats($scope.deviceId)
.succes(function (res, statusCode) {
$scope.info = res;
})
.error(function () {
alert('Oh NOS!');
})
}
});
$scope.loadInfo();
}]);

How to implement a generic HTTP service in AngularJS?

In my angular module I wrote a generic http handler for all my ajax requests.'
I was expecting that I could use the service across controllers, but my problem is the promise seems to be global.
Once ControllerOne uses the mapi_loader service, when I load AnotherController (by ng-click="go('/$route_to_load_another_controller')"), AnotherController is loaded a promise that has already returned from ControllerOne even though the URL they fetch are totally different.
So I guess my question is how do I write a service I could use across controllers? Do I really need to write a separate service for each controller where their only difference in code is the URL passed for $http.jsonp?
angular.module('myAppControllers',[])
.service('mapi_loader', ['$http', function($http) {
var promise;
var myService = {
fetch: function(url) {
if ( !promise ) {
promise = $http.jsonp(url)
.then(function (response) {
return response.data.nodes;
});
}
return promise;
}
};
return myService;
}])
.controller('ControllerOne', ['$scope', 'mapi_loader', function ($scope, mapi_loader) {
mapi_loader
.fetch("http://host.com/mapi_data_for_controller_one?callback=JSON_CALLBACK")
.then(function(data) {
$scope.useme = data;
});
}])
.controller('AnotherController', ['$scope', 'mapi_loader', function ($scope, mapi_loader) {
mapi_loader
.fetch("http://host.com/mapi_data_for_another_controller?callback=JSON_CALLBACK")
.then(function(data) {
$scope.useme = data;
});
}])
;
try something like this
angular.module('myAppControllers',[])
.service('mapi_loader', function($http) {
var alreadyLoading = {};
return {
fetch: function(url) {
if ( url in alreadyLoading ) {
return alreadyLoading[url];
}
return alreadyLoading[url] = $http.jsonp(url)
.then(function (response) {
delete alreadyLoading[url];
return response.data.nodes;
});
}
};
})
.controller('ControllerOne', function ($scope, mapi_loader) {
...
})
.controller('AnotherController', function ($scope, mapi_loader) {
...
});

Resources