I am wondering about the best way to structure the services / factories in an Angular app to consume a REST API. I know about $resource but I don't want to use it since my API is a bit special and not just CRUD on /api/resource/:id etc.
There are two basic ways I can think of and I'm not sure which is the better once. I guess there are up- and downsides for both and that's why I'm asking.
Option 1
Have an API factory with many methods for each model. That would look something like this:
angular.module('myApp', ['$http'])
.factory('apiService', function() {
var o = {
posts: [],
comments: []
};
o.getAllPosts = function() {
return $http.get('/api/v1/posts').success(function(data){
angular.copy(data, o.posts);
});
};
o.getAllComments = function() {
return $http.get('/api/v1/comments').success(function(data){
angular.copy(data, o.comments);
});
};
})
.controller('MainCtrl', function($scope, apiService) {
// Get all posts
$scope.posts = apiService.getAllPosts();
// Get all comments
$scope.comments = apiService.getAllComments();
});
Option 2
Have a factory for every data model. Something like this:
myModule.factory('posts', ['$http', function($http){
var o = {
posts: [],
post: {}
};
o.getAll = function() {
return $http.get('/api/v1/posts').success(function(data){
angular.copy(data, o.posts);
});
};
o.getOne = function(params) {
return $http.get('/api/v1/posts/'+params.hash_id).success(function(data){
angular.copy(data, o.post);
});
};
o.create = function(post) {
return $http.post('/api/v1/posts', post).success(function(data){
o.posts.unshift(data);
});
};
return o;
}]);
.controller('MainCtrl', function($scope, posts) {
// Get all posts
$scope.posts = posts.getAll();
});
What is the better way to handle this and why?
Related
I created an angular factory for $http service. I am getting the response and able to use the same in the controller but the problem is, when i check the network tab in the browser, the http request is made twice
Factory:
app.factory('myService', function ($http, $q) {
var deferred = $q.defer();
var responseData = null;
var obj = {};
obj.getData = function(){
$http.get('test.json').success(function(response){
responseData = response;
deferred.resolve(responseData);
}).error(function(response){
deferred.reject(responseData);
});
return deferred.promise;
}
obj.myData = function(){
return responseData;
}
return obj;
});
Controller:
app.controller('myController', function($scope,myService){
myService.getData().then(function(){
$scope.myDetails = myService.myData();
});
});
what's wrong in my approach. Please provide me a solution
The way you are making your caching scenario is quite complicated and not really helpful. How do you know if data has already been loaded?
Maybe you can create a simple Caching Service to handle your caching at a single point (nr of code lines will go down).
angular.module("YourApp").factory("CachingService", [
"$q",
"$http",
function ($q, $http,) {
var cache = {};
return {
getFromCache: getFromCache
};
function getFromCache(url) {
var deferred = $q.defer();
if (cache[url]) {
deferred.resolve(cache[url]);
} else {
return $http.get(url).then(function (result) {
cache[url] = result;
return result;
});
}
return deferred.promise;
}
}
]);
And then, you simply call it inside your other service :
angular.module("YourApp").factory("myService", [
"CachingService",
function(CachingService){
return {
getData: getData
};
function getData(){
return CachingService.getFromCache("test.json");
}
}
]);
And then, inside your controller :
app.controller('myController', function($scope,myService){
myService.getData().then(function(result){
$scope.myDetails = result.Data;
});
});
you can return $http not deferred.promise
I am trying to set a service and want the controllers in my app get the data from the service.
I have something like
angular.module('myApp').service('testService', ['Product',
function(Product) {
var products
//Product is a $resource object to send an http request
Product$.query({
id: 123
}, function(object) {
setProducts(object);
});
var setProducts = function(object) {
products = object;
}
var getProducts = function() {
return products;
}
return {
setProducts: setProducts,
getProducts: getProducts
};
}
]);
in my another controller
angular.module('myApp').controller('productController', ['$scope', 'testService',
function($scope, testService) {
//return undefined...
console.log(testService.getProducts())
}
]);
//testService.getProducts() returns undefined.
I think the reason is because I am making $http request and it's asynchronous so the testService has no idea what the product is when the app first loads. How do I fix this issue? Thanks so much!
I use a promise "q$" to deal with asynch calls:
angular.module('myApp').service('testService', ['Product',
function(Product, $q) {
var products
var setProducts = function(object) {
products = object;
}
var getProducts = function() {
var deferred = $q.defer();
//Product is a $resource object to send an http request
Product$.query({
id: 123
}, function(object) {
setProducts(object);
deferred.resolve(object);
});
return deferred.promise;
}
return {
setProducts: setProducts,
getProducts: getProducts
};
}
]);
angular.module('myApp').controller('productController', ['$scope', 'testService',
function($scope, testService) {
//use a promise
testService.getProducts().then(function(data){
console.log(data);
},
function (error) {
console.log(error);
})
}
]);
The promise has two call backs one for complete and one for error. You can deal with the errors in your view model as needed.
I have two REST web services which I have to call one by one. The first one return some data which I have to use for the second REST service call. The result of the second one, I need to provide it to my directive which should display the values.
I build with AngularJS a controller in this way:
app.controller('MyRestCtrl', [$scope, 'RestScope', function($scope, RestScope) {
RestScope.preGet().success(function(data) {
/* success of the first REST call and do some calculations with this data */
/* Question: I have to set the calculated data (data1 and data2) to the
RestScope provider, how can I do this, so that the second call
.get() has this data to make the request? */
RestScope.get().success(function(response) {
/* response has all the data which I need for my directive */
/* Question: How can I provide this data into my directive? */
});
})
}]);
And here is the provider:
app.provider('RestScope', function() {
this.data1 = null;
this.data2 = null;
this.setData1 = function(_data1) {
this.data1 = _data1;
}
this.setData2 = function(_data2) {
this.data2 = _data2;
}
this.$get = ['$http', '$log', function($http, $log) {
var _data1 = this.data1;
var _data2 = this.data2;
function get() {
return $http.get('http://www.test.com/' + _data1 + '/?param=' + _data2);
}
function preGet() {
return $http.get('http://www.test.com/getData/');
}
return {
get: get,
preGet: preGet
};
}];
});
And here is my directive:
app.directive('myDirective', function() {
return {
restrict: 'AEC',
replace: false,
templateUrl: 'path/to/my.html'
};
});
So the first thing is how to set inside of the preGet().success some data to the RestScope provider? And the second question if I get the data of the second call get().success how to provide it to my directive?
I have a function inside a directive that makes a query (and gets results, according to the console). The problem is that I can't seem to be able to store those results into a factory, and pass them to a controller.
This is the directive:
scope.getVersions = function(release) {
if (angular.isUndefined(release)) return;
musicInfoService.getReleaseVersions(release.id)
.success(function(data) {
dataService = data.versions;
console.log(dataService);
});
};
The console shows that dataService contains an array with the results.
Then, I try to store it into a factory:
app.factory('dataService', [function(){
return { items: [] };
}]);
And I call it in a controller:
function VersionController($scope, dataService) {
$scope.versions = dataService.items;
console.log($scope.versions);
}
But both items and $scope.versions come back an empty array. Did I miss something?
You should really use a backing field to store that data, and use setter and geter functions for writing and reading respectively:
app.factory('dataService', [function(){
var _items={};
return {
setItems:function(value){
_items=value;
},
getItems:function(){
return _items;
}
};
}]);
And for setting the data:
musicInfoService.getReleaseVersions(release.id)
.success(function(data) {
dataService.setItems(data.versions);
console.log(dataService);
});
and reading:
function VersionController($scope, dataService) {
$scope.versions = dataService.getItems();
console.log($scope.versions);
}
See demo plunk.
There's a misunderstanding of angular factories going on here. You're trying to set dataService = , which will never work.
As Mohammad mentioned, you need to have a variable set outside of your return value in the factory and the return value is basically an object with functions that allow you to manipulate your constant. So what you need is a getter "getItems()" for getting the items, and a setter "addItem(item)" for adding an item to your items array.
So you're never directly injecting your "items" into a controller, you're injecting a bunch of functions that can get or manipulate your "items".
scope.getVersions = function(release) {
if (angular.isUndefined(release)) return;
musicInfoService.getReleaseVersions(release.id)
.success(function(data) {
dataService.addItem(data.versions);
console.log(dataService.getItems());
});
};
app.factory('dataService', [function(){
var items = [];
return {
addItem: function(item) {
items.push(item);
},
getItems: function() {
return items;
}
};
}]);
function VersionController($scope, dataService) {
$scope.$watch(dataService.getItems, function(items) {
$scope.versions = items;
console.log($scope.versions);
});
}
Hey guys i'm new to angular and am not very proficient with javascript. This setup pulls in the json data fine, however when I make changes to some of the properties in the object, they reset when I change views and the controller is reloaded. Any help or guidance on how to approach this would be appreciated.
app.controller('MainCtrl', function ($scope, $location, Quiz) {
$scope.quiz = {};
Quiz.getQuestions(function(data) {
$scope.quiz = data;
});
});
app.service('Quiz', function($http) {
this.getQuestions = function(callback) {
$http.get('questions/questions.json').success(function(data) {
if (callback) {callback(data);}
return data;
});
};
});
Does the $http get request get repeated on subsequent calls to getQuestions() overwriting the object? If so, perhaps
app.service('Quiz', function($http) {
var _data;
this.getQuestions = function(callback) {
if (_data) {
callback(_data);
}
$http.get('questions/questions.json').success(function(data) {
_data = data;
if (callback) {callback(data);}
return data;
});
};
});