So, over the last month I've been diving pretty hard into AngularJS and my controllers got extremely big. I decided to learn about factories and services so I could separate some of my logic...which leads me to my question.
I see a lot of examples online where people are using ajax calls to the server from their controllers. Is this a good practice? The more I study AngularJS and compare it to what I already know about software design, I see the controller's job pass data into the view. Much like a controller in MVC, where business logic shouldn't exist. Is that a safe assumption?
Also -- If I were to move my ajax calls over to a factory or service, would the functionality be the same? Something like below?
Service
app.service('orderService', ['$http', function ($http) {
var orderdata = {};
// Gets orders from WebAPI
this.getRecentOrders = function () {
$http.get('/api/orders').success(function (data) {
this.orderdata = data;
});
return this.orderdata;
};
}]);
Controller
$scope.recentOrders = orderService.getRecentOrders();
edit
I had to modify the code in the controller slightly from the answer to get it to work correctly:
orderService.getRecentOrders().then(function(result) {
$scope.recentOrders = result.data;
});
Services should return an object with methods. Also, the method you invoke should return the promise as it's an async call, so the return this.orderdata will always be undefined (or at least the first time):
app.service('orderService', ['$http', function ($http) {
return {
getRecentOrders: function () {
return $http.get('/api/orders');
}
};
}]);
No with this, you can chain to that promise in order to get the data in your controller:
orderService.getRecentOrders().then(function(data) {
$scope.recentOrders = data;
});
Now as per the question itself, basically, controllers should be "dumb", what does this mean? it should not have any heavy logic within. It should only be the layer that gets data from the services, and use them to show them in the $scope. Everything related to heavy logic (data processing and such) should be apart from the controller. That's why angular provides you with Filters, Services and Directives.
Related
I have such working code:
Service (factory?):
myApp.factory('MyService', ['$q','$resource', 'MyResource',
function ($q, $resource, MyResource) {
return {
getRoles: function () {
return MyResource.getRoles(); //MyResource makes API calls using $resource
} } });
Controller:
$scope.Roles = MyService.getRoles();
$scope.Roles.$promise.then(function () { $scope.RolesCount = $scope.Roles.length; });
What I'd like to do is to create such function in MyService that will return number of roles once getRoles is resolved so I can use it in controller like this:
$scope.RolesCount = MyService.getRolesCount();
and then in html
{{RolesCount}}
Unfortunately, I have no idea how to do this since my getRolesCount() method needs to return something so I can't use $promise.then() inside MyService. I'll try to update my question's title once I come up with something more accurate.
If server response needs to be transformed in a service, then then should be moved there. However, it's not practical or possible if then is supposed to unwrap a promise and assign a value to scope, like in the code above.
$resource returns an object that is filled on promise resolution. The way a resource is supposed to be used in view is:
{{Roles.length}}
When an object is updated, it will be updated in view, too. Assigning the value to another variable is inefficient.
It's impossible to do something like
$scope.RolesCount = MyService.getRolesCount();
because getRolesCount is asynchronous, and the value it resolves with is scalar. It is possible to flatten it with `async..await, (TypeScript or ES2017), but will require additional measures to synchronize control flow with AngularJS digests, as explained in this answer.
I was taught that we use factories/services to eliminate the duplicate coding. Here's a part of the code which works fine.
app.controller('ServicesCtrl',['$scope','DataFactory',function($scope,$http,DataFactory){
DataFactory.GetData('services1.json')
.then(function(response){
$scope.returnedData = response.data;
})
.catch(function(response){
console.log('Error in process',response.status,response.data);
});
}]);
app.controller('ContactCtrl',['$scope','DataFactory', function($scope,DataFactory){
DataFactory.GetData('location.json')
.then(function(response){
$scope.returnedData = response.data;
})
.catch(function(response){
console.log('Error in process',response.status,response.data);
});
}]);
app.factory('DataFactory',['$http',function($http){
var factory = {};
factory.GetData = function(path) {
return $http.get(path);
}
return factory;
}]);
My question is 1. Why use services/factories to make such ajax calls when we have to work on the promises inside controllers? I mean, I have to make the same .then and .catch calls in both the controllers here. Where is the efficiency in it? Is there any better way to do this? Or am I doing this wrong? Is it possible to to work on those promises inside the factories and return the response.data to different controllers?
The thing here is re-usability of code . Now suppose you have a service called
angular.module('test')
.service('testService', function ($http) {
this.getBidsUser = function ($username) {
var endpoint = "bids/users/"+$username;
return $http({
method: 'get',
url: endpoint
});
};
}
which returns bids of a user . You might want to use the same information in different views. So its a good idea to use service so that you dont have to rewrite same code again .
Once more you might want to have all the same end point related service on the same service for maintainability .
You might need to change end points for a service which will be hectic if you do not use service pattern .
Promises arr call backs . If you process those promises inside a service it will not be easy to maintain caller timeline .
Hi we use factories and services
to make the application more modular,
to have the possibility to reuse the code,
to hide the implementation detail
For example a service which makes an http call it may be seen as an high level "service" which gives you back the required object, indipendently by the call type and it's reusable at high level.
The service allows to customize the call parameters, maybe avoiding that some of them are to be specified by the calling controller. Or it can do some preprocessing or post processing, it can do some caching and so on. And its portable, so you can call it each time you need.
For your last question
Is it possible to to work on those promises inside the factories and
return the response.data to different controllers?
Maybe it's possible, but may be complex to implement as it has to do with the timing of the response. Instead i suggest you the $resource service in the module ngResource, which can already do what you neeed.
See docs:
AngularJs Tutorial on $resource
Angularjs Programming guide on $resource
To share data between controllers, most of the Stack overflow Answers suggest to use services. Mostly when I share data between controllers, it is my application model(Data) and it changes in each controller as per application logic. So, should it not be an angular value instead of angular service?
For example, take the following service,
app.factory('Employee',function($http){
function Employee(){
this.data = {};
}
Employee.prototype.load = function(){
//XHR call which invokes employee details and assigns it here
$http.get(url).then(
function(response){
this.data = response.data;
}
);
}
return new Employee();
});
With this service in hand, I would not be able to inject my Employee model during ui-router's resolve(as services cannot be injected into config blocks). But if I create the same using value, I would be able to inject it during stateRouting itself. Could you please give me why value is not preferred to create models/share data between controllers over service?
First, values can't be injected into config blocks either. But that's irrelevant, since resolve functions are not called during the config phase, but at runtime, every time you navigate to the enclosing route.
Values can't be injected at all, so I don't really see how you would have access to $http when defining your value.
Finally, you can access your service in a resolve function, simply by injecting it into the function:
resolve: {
employee: function(Employee) {
return Employee.load();
}
}
But that would not make much sense, since your load() method doesn't return anything. What it should do is returning a promise of employee:
Employee.prototype.load = function(){
return $http.get(url).then(
function(response) {
return response.data;
}
);
};
But that has nothing to do with sharing data between controllers. What it allows doing is waiting for the employee data to be available and injecting it in the controller before switching to the employee view.
I'm a bit confused with Angular. I have two factories, with code looks almost the same, because they performs CRUD operations on two different objects in db, and I want to make them DRY.
So I have idea to move common logic to separate service, and I want it to works something like that :
angular.module('app').factory('first',['commonService',function(commonService){
return new commonService('someSpecificVariable');
}])
and service :
angular.module('app').service('commonService',['someDep1',function(someDep1,someSpecificVariable){
var something = someSpecificVariable;
}]);
I looked at providers, but i need something to instantiate. How can I achieve this?
In another words I want create factory responsible for all crud operation requests for all app modules, because writing many factories just to handle http/crud don't looks ok for me.
Ok i descriped it quite bad.
SOLUTION Is it possible and in good form to reuse the same data factory in Angular?
Factories
They let you share code between controllers as well as making http calls to your API. They are really about making some reusable code that you can instantiate in your controllers to make your life easier and your controllers cleaner.
Simple Example
.factory('FindFriend', function ($http, $rootScope) {
return {
find: function (phone) {
return $http.get('http://130.211.90.249:3000/findFriend', { params: {phone:phone}})
},
add: function (id) {
return $http.get('http://130.211.90.249:3000/addFriend', { params: {friendid:id, user_id: $rootScope.session} })
},
deleteFriend: function (id) {
return $http.get('http://130.211.90.249:3000/deleteFriend', {params:{idfriends: id}})
}
}
})
Explanation
So above we can see a factory called FindFriend. The factory has 3 methods find add and delete. these are different http calls (in your code they shouldn't be all get methods but this is some old code I wrote).
They can be instantiated by adding them into the top of the controller and then calling there functions like FindFriend.add
Hope this sheds some light on factories for you.
I know how factories works, but i dont want to add bunch of functions responsible for each module. I wish to make service, which will replace patches to $http calls based of provided module name in constructor. ex 'orders' will make request :
$http.post('/api'+'orders'+'/lazy')...
So, this isn't the typical question of HOW to do it. I know it can be done with a service or a factory but I was wondering if someone could share what the advantages/disadvantages would be of just creating a basic service, injecting it into each controller, and then extending the service with functions from each controller. Something similar to the following example..
app.service('HelperService', function() {
return {};
});
app.controller('Controller1', function($scope, HelperService) {
$scope.somefunc = function() {
//do stuff here
};
HelperService.somefunc = $scope.somefunc;
});
app.controller('Controller2', function($scope, HelperService) {
HelperService.somefunc();
});
This works, and works well. I feel a bit stupid for asking this but it just seems like I'm missing something here as to why this isn't used or recommended?
It may work, but its a bad idea.
Controller2 HelperService.somefunc() won't exist until Controller1 has been instantiated. So theres an implicit dependency of Controller2 on Controller1
the code on HelperService isn't in one place where it can be understood together
if you are doing some sort of data manipulation in that function, it really should be operating on data encapsulated by the HelperService.
The service is a singleton and it will be instantiated once calling new on the function itself -- the function you pass in is essentially a constructor. This will create the empty object you are returning for use everywhere, but if you want to return an object in such a way it makes more sense to use .factory, but this is not a big deal.
At any rate, you can consider your code to conceptually do this:
var HelperService = function () {}
var helperService = new HelperService;
function Controller1() {
helperService.someFunc = function () {}
}
function Controller2() {
helperService.someFunc();
}
I would consider this a dangerous thing to do for a couple of reasons:
Controller1 must be instantiated before Controller2 or else somefunc won't be available to Controller2. Ideally the controllers would have no knowledge of each other.
You are coupling Controller/ViewModel (since you're using scope) with service level logic, but these should be decoupled. HelperService shouldn't know about the controllers either. Instead, you should be injecting a service that has an API that the controllers expect to use. This doesn't always have to be HelperService, it just has to look like HelperService to the controllers and its API shouldn't change.
Without knowing specifics about what you're trying to do, it's hard to advise. In general you may rethink what you want to do, but you can extend functionality of services with other services. Consider services to be in their own layer.