How to implement a generic HTTP service in AngularJS? - 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) {
...
});

Related

Angularjs - Editing arrays returned from http get url

I have this array I am getting through the following method:
var url= *url defined here*;
$scope.ViewProfile = function () {
$http.get(url)
.success(function (response) {
$scope.ProfileList = response;
$scope.FavNumbers = $scope.ProfileList[0].FavNumbers;
})
.error(function () {
});
}
I am required to edit the Fav Numbers list on the UI. and post it back to another url through http post url method. What I am stuck is with the concept of asynchronous calls, due to which I am unable to retrieve the favorite numbers list to be available for editing. Please help!
I have tried a method of using promises as follows:
app.factory('myService', function($http) {
var myService = {
async: function(url) {
var promise = $http.get(url).then(function (response) {
console.log(response);
return response.data;
});
// Return the promise to the controller
return promise;
}
};
return myService;
});
In my controller I am doing:
angular.module('JuryApp').controller('mycontroller', ['myService', function (myService) {
myService.async(url).then(function(d) {
$scope.data = d;
});
app.controller('MainCtrl', function( myService,$scope) {
// Call the async method and then do stuff with what is returned inside our own then function
myService.async().then(function(d) {
$scope.data = d;
});
});
But I keep getting the error 'd is not defined'. It keeps giving an error of some sort, where the debugger goes into an infinite loop or something.
You are overcomplicating it, I think. Async calls are actually pretty simple:
You're service:
app.factory("myService", ["$http", function($http) {
var MyService = {
getData: function(url) {
return $http.get(url); //$http returns a promise by default
}
};
return MyService;
})];
Your controller:
angular.module('JuryApp').controller('mycontroller', ['myService', function (myService) {
$scope.FavNumbers = [];
var url = "http://my.api.com/";
myService.getData(url).then(function(response) {
$scope.FavNumbers = response.data[0].FavNumbers;
});
}]);
That's all that you need to do.

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;
});
};
}])

AngularJS : returning data from service to controller

I am trying to create a service to get json and pass it to me homeCtrl I can get the data but when a pass it to my homeCtrl it always returns undefined. Im stuck.
My Service:
var myService = angular.module("xo").factory("myService", ['$http', function($http){
return{
getResponders: (function(response){
$http.get('myUrl').then(function(response){
console.log("coming from servicejs", response.data);
});
})()
};
return myService;
}
]);
My Home Controller:
var homeCtrl = angular.module("xo").controller("homeCtrl", ["$rootScope", "$scope", "$http", "myService",
function ($rootScope, $scope, $http, myService) {
$scope.goData = function(){
$scope.gotData = myService.getResponders;
};
console.log("my service is running", $scope.goData, myService);
}]);
You should return promise from getResponders function, & when it gets resolved it should return response.data from that function.
Factory
var myService = angular.module("xo").factory("myService", ['$http', function($http) {
return {
getResponders: function() {
return $http.get('myUrl')
.then(function(response) {
console.log("coming from servicejs", response.data);
//return data when promise resolved
//that would help you to continue promise chain.
return response.data;
});
}
};
}]);
Also inside your controller you should call the factory function and use .then function to get call it when the getResponders service function resolves the $http.get call and assign the data to $scope.gotData
Code
$scope.goData = function(){
myService.getResponders.then(function(data){
$scope.gotData = data;
});
};
This is an example how I did for my project, it work fine for me
var biblionum = angular.module('biblioApp', []);//your app
biblionum.service('CategorieService', function($http) {
this.getAll = function() {
return $http({
method: 'GET',
url: 'ouvrage?action=getcategorie',
// pass in data as strings
headers: {'Content-Type': 'application/x-www-form-urlencoded'} // set the headers so angular passing info as form data (not request payload)
})
.then(function(data) {
return data;
})
}
});
biblionum.controller('libraryController', function($scope,CategorieService) {
var cat = CategorieService.getAll();
cat.then(function(data) {
$scope.categories = data.data;//don't forget "this" in the service
})
});

How to store data from http service in angular factory

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);
});
}]);

combining scope from two controllers

I have two controllers/views, one called member.js which is for displaying a list of members projects:
.controller( 'MemberCtrl', function MemberCtrl( $scope,ProjectsService ) {
$scope.projects = [];
$scope.refresh = function() {
ProjectsService.query()
.then(function (data) {
$scope.projects = data;
});
};
$scope.refresh();
})
.factory('ProjectsService', ['$http', '$q', function($http, $q) {
return {
query: function() {
var deferred = $q.defer();
$http.get('/api/get-projects')
.success(function(data) {
deferred.resolve(data);
})
.error(function(data) {
deferred.reject(data);
});
return deferred.promise;
}
};
}])
The next controller is in add.js and handles creating a new project:
.controller( 'AddprojectCtrl', function AddprojectCtrl( $http,$scope,Session,$location ) {
$scope.addProject = function(project){
return $http
.post('/api/create-project', project)
.then(function (result) {
$location.path("/member");
});
};
})
The issue I have is that once i've added in the new project, $scope.projects on the MemberCtrl is out of date and doesn't show the newly added item. I guess I could use one controller for both actions but was wondering if this is the best approach or what the "anggular" way is?
Your AddprojectCtrl should not be a controller, rather it should be a factory
.factory( 'AddprojectCtrl', function AddprojectCtrl( $http,$location ) {
return {
addProject:function(project) {
$http.post('/api/create-project', project)
//Ideally this can be done inside the controller itself
.then(function (result) {
$location.path("/member");
}
}
})
And then you need to inject the factory inside the controller. In the controller then you can call the addProject function of the factory. Thats the angular way :)

Resources