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();
}]);
Related
I'm new to angular and I've been told that it's better to make services do the heavy lifting for the controllers, but I'm finding it diffcult to make use of the services I've created. I've seen several questions on this but I can't find solutions.
Here's my service
(function () {
'use strict';
var office = angular.module('Office', []);
office.factory('auth', ['$http', '$localForage', '$scope', function ($http, $localForage, $scope) {
var auth = {}
auth.login = function (credentials) {
$http.post('/auth_api/login', credentials)
.then(function (data) {
$localForage.setItem('user', data.data)
},
function () {
$scope.login_error = 'Invalid Username/password'
})
}
$localForage.getItem('user').then(function (data) {
auth.isAuthenticated = !!data.id
})
return auth
}])
And here's my controller
office.controller('LoginController', ['$scope', 'auth', function ($scope, auth) {
$scope.login = auth.login($scope.user)
}])
I have created a simpler version from your code.. and its working here . check the link - fiddle
app.factory('auth', ['$http', function ($http) {
var auth = {};
auth.login = function (credentials) {
return "success";
}
return auth;
}]);
replace login function with your implemenation
Your code is correct but as you are not returning anything from the "auth" factory, you are not getting any update in the controller. Change your code as shown below to return the data from factory or any message acknowledging the login.
Factory :
(function () {
'use strict';
var office = angular.module('Office', []);
office.factory('auth', ['$http', '$localForage', '$scope', function ($http, $localForage, $scope) {
var auth = {}
auth.login = function (credentials) {
return $http.post('/auth_api/login', credentials)
.then(function (data) {
$localForage.setItem('user', data.data);
setAuthentication(true);
return data.data;
},
function (err) {
return err;
});
}
auth.setAuthentication = function (isLoggedIn){
this.isAuthenticated = isLoggedIn;
}
return auth;
}]);
Controller :
office.controller('LoginController', ['$scope', 'auth', function ($scope, auth) {
$scope.login = function (){
auth.login($scope.user).then(function (data){
$scope.userDetails = data;
}, function (err){
$scope.loginError = 'Invalid Username/password';
});
}
}]);
I'm trying to built a service for loading json files. What am I doing wrong?
The Service
app.service("jsonService", function ($http, $q)
{
var deferred = $q.defer();
$http.get('./assets/json/home.json').then(function (data)
{
deferred.resolve(data);
});
this.getHomeItems = function ()
{
return deferred.promise;
}
})
My Controller
app.controller('homeController', function ($scope, jsonService) {
var promise = jsonService.getHomeItems();
promise.then(function (data)
{
$scope.home_items = data;
console.log($scope.home_items);
});
});
Console Error: $scope is not defined
You are missing the dependency injection.
Your service should be:
app.service("jsonService", ["$http", "$q", function ($http, $q)
{
var deferred = $q.defer();
$http.get("./assets/json/home.json").then(function (data)
{
deferred.resolve(data);
});
this.getHomeItems = function ()
{
return deferred.promise;
}
}]);
And your Controller:
app.controller("homeController", ["$scope", "jsonService", function ($scope, jsonService)
{
var promise = jsonService.getHomeItems();
promise.then(function (data)
{
$scope.home_items = data;
console.log($scope.home_items);
});
}]);
Without looking at the HTML, which you did not provide, I reckon you may not have injected the $scope into your controller constructor:
app.controller('homeController', ['$scope', function ($scope, jsonService) {
...
}]);
Theoretically, AngularJS should be able to infer the dependency from the variable name, but according to the official documentation there are circumstances where this does not work and the practice of not explicitly injecting dependencies is discouraged.
So you may want to try explicit injection (as shown above).
See the examples on the official docs here:
https://docs.angularjs.org/guide/controller
and here:
https://docs.angularjs.org/guide/di
You are have a common anti-pattern where you are unwrapping the promise returned by $http and then re-wrapping the data in a promise. This is unnecessary, just return the promise returned by $http.
this.getHomeItems = function () {
return $http.get("./assets/json/home.json");
}
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);
});
}]);
I'm trying to run a function inside .factory to get posts, but I get an undefined error on my controller.
.factory('Portfolio', function($http, $log) {
//get jsonp
this.getPosts = function($scope) {
$http.jsonp("http://test.uxco.co/json-test.php?callback=JSON_CALLBACK")
.success(function(result) {
$scope.posts = $scope.posts.concat(result);
$scope.$broadcast("scroll.infiniteScrollComplete");
});
};
});
.controller('PortfolioCtrl', function($scope, Portfolio) {
$scope.posts = [];
Portfolio.getPosts($scope);
Thanks!
What you wrote is a service not a factory. so you either
change it to be .service instead of .factory.
or return the function inside an object like this
.factory('Portfolio', function($http, $log) {
//get jsonp
return {
getPosts: function($scope) {
$http.jsonp()
.success(function(result) {
$scope.posts = $scope.posts.concat(result);
$scope.$broadcast("scroll.infiniteScrollComplete");
});
};
}
});
check the difference between service and factory
Use .factory code like this
.factory('Portfolio', function($http, $log) {
//get jsonp
return{
getPosts: function($scope) {
$http.jsonp("http://test.uxco.co/json-test.php?callback=JSON_CALLBACK")
.success(function(result) {
$scope.posts = $scope.posts.concat(result);
$scope.$broadcast("scroll.infiniteScrollComplete");
});
};
}
});
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) {
...
});