I've come across many articles that shows how to separate business logic from controller and keep them in separate layers. As for angular, we add all the logic in our services,factories etc.
But I've come across the following line of code
angular.module('myApp').controller(function($scope,$userService) {
$scope.users = $userService.get('/users');
$scope.add = function() {
// do something
};
});
and people still argue that we are still adding logic in our controllers. If that's true, then what will be the best way to initialize data in my controller so that I can avoid having logic in my controllers OR any best practices that can help me achieve those.
P.S I'm requesting suggestions strictly for Angular.
There is definitely "logic" in a controller, but the logic should be limited to defining the ViewModel and changing it by reacting to events from the View and from the Model.
The logic deals with the state of the app, or a portion of the app's view for which the controller is authoritative for.
The logic that should not be in the controller has to do with backend knowledge, manipulation of the model's data, manipulation of the View / DOM, business logic that doesn't directly relates to how the data is staged for presentation.
Your example is fine, except for the "/users" part, which could benefit from being abstracted away in the service.
In the code example given, you could make use of a resource (https://docs.angularjs.org/api/ngResource/service/$resource) to abstract the URL detail. The code might then look as follows:
angular.module('myApp').controller(function($scope,Users) {
$scope.users = Users.query();
$scope.add = function() {
// do something
};
});
I'm assuming that the the add function is linked to an ng-click event with the template, in which case that looks ok. If in place of // do something there was a a lot of code, that could perhaps be moved into a Service.
Taken from the AngularJS Docs:
Controllers are "classes" or "constructor functions" that are
responsible for providing the application behavior that supports the
declarative markup in the template.
As a rule of thumb, if it's not application behavior e.g. updating the model, handling a click event etc then abstract into a service.
Related
We have a large Angularjs 1.6 application that has $rootscope scattered throughout the app in over 200 places in filters, services, routes, etc.. so it needs to be refactored, but I'm not sure how to know when to remove it. When is it a best practice to use $rootscope in the application?
I've read everything from never, to using it for storing variables, which I assumed was for sharing data between controllers. I've since read that it's better to use factories/services for this use case instead and I also read that one valid use case is to use $rootscope as a global event bus.
I didn't really see this explained in the Angularjs docs.
From ng-book:
When Angular starts to run and generate the view, it will create a binding from the root ng-app
element to the $rootScope. This $rootScope is the eventual parent of all $scope objects.
The $rootScope object is the closest object we have to the global context in an
Angular app. It’s a bad idea to attach too much logic to this global context, in the
same way that it’s not a good idea to dirty the JavaScript global scope.
You are right, you should definitely use Services to share data and logic between your modules.
Putting a lot of logic in your $rootScope means having bad maintainability and modularity in your application, it is also very difficult to test issues.
I highly suggest you to take a look at:
Services AngularJS Documentation
Thinkster brilliant article on how to share data between controllers
Screencast by Simpulton
#Breck421 answer to this question
I know it may be easy to attach everything to $rootScope, but It is just difficult to work on it, make little changes, reusing your code for other applications or modules and test your application in general.
EDIT
Recently I had to fetch some items from API and catch these items in order to show them in a certain view. The item fetching mechanism was in a certain Factory, while the mechanism to format and show the items was in a Controller.
So, I had to emit an event in the Factory when items got fetched and catch this event in the Controller.
$rootScope way
//Factory
$rootScope.$broadcast('refreshItems', items);
//Controller
$scope.$on('refreshItems', doSomething());
It clearly worked but I didn't really like to use $rootScope and I've also noticed that the performance of that task were pretty miserable.
Then I tried giving a shot to Postal.js:
Postal.js is an in-memory message bus - very loosely inspired by AMQP -
written in JavaScript. Postal.js runs in the browser, or on the server
using node.js. It takes the familiar "eventing-style" paradigm (of
which most JavaScript developers are familiar) and extends it by
providing "broker" and subscriber implementations which are more
sophisticated than what you typically find in simple event
emitting/aggregation.
I tried using Postal.js for this kind of needs and I found out that it is really faster than using $rootScope for this purpose.
//Factory
$scope.$bus.publish({
channel : 'reloadItems',
topic : 'reloadItems'
data : items
);
//Controller
$scope.$bus.subscribe({
channel : 'reloadItems',
topic : 'reloadItems',
callback : function () {
resetAndLoadItems();
}
});
I hope I've been helpful.
From Angluar docs: Every application has a single root scope. All other scopes are descendant scopes of the root scope. Scopes provide separation between the model and the view, via a mechanism for watching the model for changes.
Of course this is going to come down to a matter of opinion and style. I tend to follow a style very close to John Papa's Angular Style Guide.
In keeping with the two, and following a good separation of concerns strategy my architecture contains factory models that are shared across the application. My controllers in turn are all bound to the services that hold the shared data.
Using $rootScope as the global event bus is exactly how Angular uses it. Should you tag along and do the same? I don't see why not. But if you are, make sure that the purpose is clearly defined and maybe even use your own service to register events to the global event bus. That way you are decoupling your app from Angular, and if you ever decide that you want to change the framework in which your global event bus lives then you can change it in one place.
This is what I'm suggesting:
Global event bus
// Angular specific: add service to module
angular.module('app').factory('globalEventBus', GlobalEventBus);
// Angular specific: inject dependencies
GlobalEventBus.$inject(['$rootScope']);
// Non framework specific.
// param: fameworkEventBus will be $rootScope once injected
function GlobalEventBus(fameworkEventBus) {
var globalEventBus = this;
globalEventBus.registerEvent(params...){
fameworkEventBus.
}
return globalEventBus;
}
Global data models
My data models are smart and tend to contain functions that provide information about themselves or retrieve/return specific data.
// Angular specific: add service to module
angular.module('app').factory('dataModel', DataModel);
function DataModel() {
var dataModel= this;
dataModel.myData = {};
dataModel.GetSpecificData = funtion(param){
return ...
}
return dataModel;
}
The controller
// Angular specific
angular.module('app').controller('MyController', MyController);
// Angular specific: inject dependencies to controller
MyController.$inject = ['dataModel'];
// By convention I use the same parameter name as the service.
// It helps me see quickly if my order of injection is correct
function MyController(dataModel) {
var myController = this;
// Bind to the service itself, and NOT to the service data property
myController.myData = dataModel;
myController.doStuff = function(){
}
}
Here is a fun post about binding to services and not to service properties.
All in all you have to be the judge of what works best for you. A good system architecture and good style have saved me countless hours of solving completely avoidable problems.
After doing some more work with Angular and more reading I found this basic rule of thumb for using $rootscope that I wanted to add to the other answers:
Only add properties that are static or constant. Anything else that
represents a changing state or a mutable value should have a
corresponding directive or controller to handle it.
We have a large Angularjs 1.6 application that has $rootscope scattered throughout the app in over 200 places in filters, services, routes, etc.. so it needs to be refactored, but I'm not sure how to know when to remove it. When is it a best practice to use $rootscope in the application?
I've read everything from never, to using it for storing variables, which I assumed was for sharing data between controllers. I've since read that it's better to use factories/services for this use case instead and I also read that one valid use case is to use $rootscope as a global event bus.
I didn't really see this explained in the Angularjs docs.
From ng-book:
When Angular starts to run and generate the view, it will create a binding from the root ng-app
element to the $rootScope. This $rootScope is the eventual parent of all $scope objects.
The $rootScope object is the closest object we have to the global context in an
Angular app. It’s a bad idea to attach too much logic to this global context, in the
same way that it’s not a good idea to dirty the JavaScript global scope.
You are right, you should definitely use Services to share data and logic between your modules.
Putting a lot of logic in your $rootScope means having bad maintainability and modularity in your application, it is also very difficult to test issues.
I highly suggest you to take a look at:
Services AngularJS Documentation
Thinkster brilliant article on how to share data between controllers
Screencast by Simpulton
#Breck421 answer to this question
I know it may be easy to attach everything to $rootScope, but It is just difficult to work on it, make little changes, reusing your code for other applications or modules and test your application in general.
EDIT
Recently I had to fetch some items from API and catch these items in order to show them in a certain view. The item fetching mechanism was in a certain Factory, while the mechanism to format and show the items was in a Controller.
So, I had to emit an event in the Factory when items got fetched and catch this event in the Controller.
$rootScope way
//Factory
$rootScope.$broadcast('refreshItems', items);
//Controller
$scope.$on('refreshItems', doSomething());
It clearly worked but I didn't really like to use $rootScope and I've also noticed that the performance of that task were pretty miserable.
Then I tried giving a shot to Postal.js:
Postal.js is an in-memory message bus - very loosely inspired by AMQP -
written in JavaScript. Postal.js runs in the browser, or on the server
using node.js. It takes the familiar "eventing-style" paradigm (of
which most JavaScript developers are familiar) and extends it by
providing "broker" and subscriber implementations which are more
sophisticated than what you typically find in simple event
emitting/aggregation.
I tried using Postal.js for this kind of needs and I found out that it is really faster than using $rootScope for this purpose.
//Factory
$scope.$bus.publish({
channel : 'reloadItems',
topic : 'reloadItems'
data : items
);
//Controller
$scope.$bus.subscribe({
channel : 'reloadItems',
topic : 'reloadItems',
callback : function () {
resetAndLoadItems();
}
});
I hope I've been helpful.
From Angluar docs: Every application has a single root scope. All other scopes are descendant scopes of the root scope. Scopes provide separation between the model and the view, via a mechanism for watching the model for changes.
Of course this is going to come down to a matter of opinion and style. I tend to follow a style very close to John Papa's Angular Style Guide.
In keeping with the two, and following a good separation of concerns strategy my architecture contains factory models that are shared across the application. My controllers in turn are all bound to the services that hold the shared data.
Using $rootScope as the global event bus is exactly how Angular uses it. Should you tag along and do the same? I don't see why not. But if you are, make sure that the purpose is clearly defined and maybe even use your own service to register events to the global event bus. That way you are decoupling your app from Angular, and if you ever decide that you want to change the framework in which your global event bus lives then you can change it in one place.
This is what I'm suggesting:
Global event bus
// Angular specific: add service to module
angular.module('app').factory('globalEventBus', GlobalEventBus);
// Angular specific: inject dependencies
GlobalEventBus.$inject(['$rootScope']);
// Non framework specific.
// param: fameworkEventBus will be $rootScope once injected
function GlobalEventBus(fameworkEventBus) {
var globalEventBus = this;
globalEventBus.registerEvent(params...){
fameworkEventBus.
}
return globalEventBus;
}
Global data models
My data models are smart and tend to contain functions that provide information about themselves or retrieve/return specific data.
// Angular specific: add service to module
angular.module('app').factory('dataModel', DataModel);
function DataModel() {
var dataModel= this;
dataModel.myData = {};
dataModel.GetSpecificData = funtion(param){
return ...
}
return dataModel;
}
The controller
// Angular specific
angular.module('app').controller('MyController', MyController);
// Angular specific: inject dependencies to controller
MyController.$inject = ['dataModel'];
// By convention I use the same parameter name as the service.
// It helps me see quickly if my order of injection is correct
function MyController(dataModel) {
var myController = this;
// Bind to the service itself, and NOT to the service data property
myController.myData = dataModel;
myController.doStuff = function(){
}
}
Here is a fun post about binding to services and not to service properties.
All in all you have to be the judge of what works best for you. A good system architecture and good style have saved me countless hours of solving completely avoidable problems.
After doing some more work with Angular and more reading I found this basic rule of thumb for using $rootscope that I wanted to add to the other answers:
Only add properties that are static or constant. Anything else that
represents a changing state or a mutable value should have a
corresponding directive or controller to handle it.
Currently I have a bunch of controllers that seems a little too bulky for my liking in that they manage both the logic and the storing of data.
For instance let us take a over simplified register controller:
$scope.username = "";
$scope.password = "";
$scope.validateUsername= function(){
......
}
$scope.validatePassword= function(){
......
}
$scope.updateUserConfig = function(){
//a rest call here
}
ok so to me this seems like it is wrong because the controller is storing data, performing logic and making calls to a rest service.
I thought the controller should be the logic for the view it is used for while the "model" i.e. username and password should be somewhere else as well as the call to the rest service.
I have looked about and have seen people employing factories that are injected into the controller for the rest calls; as these factories will not store state they can be shared across the entire application, so this seems like a good idea ?
I am unsure about the data aspect though, is it normal for data to be stored within the controller or is there a better practice for this?
It is important to remember that your view must bind to the data/methods in the controller, so it must be exposed in such a way for this to be possible. In other words, there's nothing wrong with having these properties in your controller.
That being said, this data often comes from an external source or is needed in multiple views/controllers so it's common for a factory/service to return an object to a controller and then assign that object to a $scope property. This way you can abstract that layer away and share the data across your application without repeating yourself.
Pardon me if this sounds stupid but I have been using AngularJS for a while now and everywhere I have seen people telling me to wrap my logic in a directive(or service ?) instead of my controller and keep only the bindings in my controller. Apart from the reusability aspect of a directive is there any other reason ?
Until now I haven't actually understood why this is the case. Doesn't writing a directive come with a lot of overhead ? I haven't faced any kind of problems writing logic in my controller and it is EASY. What ARE the downfalls of this approach ?
The controller is the right place to do all and everything that is related to the scope. It is the place where you write all the
$scope.$watch(...)
and define all the $scope functions that you need to access from your views ( like event handlers ). Generally, the event handler is a plan function which will in turn call a function a service.
$scope.onLoginButtonClick = function(){
AuthenticationService.login($scope.username,
$scope.password);
};
On very rare occasions you can add a promise success handler in there.
DONT: Write business logic in controllers
There was a very specific reason why the earlier example was like that. It showed you a $scope function that was in turn calling a function in a service. The controller is not responsible for the login mechanism or how login happens. If you write this code in a service, you are decoupling the service from the controller which means anywhere else that you want to use the same service, all that you need to do is, inject and fire away the function.
Rules for the Controller going forward:
Controllers should hold zero logic
Controllers should bind references to Models only (and call methods returned from promises)
Controllers only bring logic together
Controller drives Model changes, and View changes. Keyword; drives, not creates/persists, it triggers them!
Delegate updating of logic inside Factories, don't resolve data inside a Controller, only update the Controller's value with updated Factory logic, this avoids repeated code across Controllers as well as Factory tests made easier
Keep things simple, I prefer XXXXCtrl and XXXXFactory, I know exactly what the two do, we don't need fancy names for things
Keep method/prop names consistent across shared methods, such as this.something = MyFactory.something; otherwise it becomes confusing
Factories hold the Model, change, get, update, and persist the Model changes
Think about the Factory as an Object that you need to persist, rather than persisting inside a Controller
Talk to other Factories inside your Factory, keep them out the Controller (things like success/error handling)
Try to avoid injecting $scope into Controllers, generally there are better ways to do what you need, such as avoiding $scope.$watch()
There two good reasons for me for keeping logic out of a controller:
Reusability
If your application has multiple controllers and each do pretty much the same thing with some differences then keeping logic in the controller means you will be repeating the code you write. It's better if you Don't Repeat Yourself. By putting that logic into a service you can inject the same code into multiple controllers. Each service (really a Factory) is created as a new instance of itself each time it is injected into a controller. By pushing logic into a service you can modularise your code, which keeps it easier to maintain and test (see below)
Testing
Good code is tested. Not just by people but by the unit tests you write. Unit tests give you as a developer assurance that your code does what you expect it too. They also help you design your code well.
If your controller has 20 different methods each with their own logic, then testing (and your code) is turning into spaghetti.
It's easier to write unit tests that are narrow i.e. they test one thing at a time. And fortunately it's also good (for the reasons outlined above) to break your code up into encapsulated pieces i.e. they do one thing and can do it in isolation. So unit tests (especially if you write your tests first) force you into thinking about how to break up your code into maintainable pieces, which leaves your application in a good state if you want to make changes in the future (you run the unit tests and can see where things break).
Example
Form application:
You have a form application serving multiple forms. You have a controller for each form. When the user submits the form the data is sent via a proxy to a CRM that stores the information in a database.
If a customer already exists in the CRM you don't want to create duplicates (yes the CRM should handle data cleansing but you want to avoid that where possible). So once the user submits their form data something needs to implement logic that goes something like:
search for the user in the CRM via an API endpoint
if the user exists get the user ID and pass it with the form data to another endpoint
if they don't exist hit another endpoint and create a new user, get the user ID and send it and the form data to associate it with the user
NB: Arguably all of the above should be done by a back-end service but for the sake of example let's go with it.
Your application has multiple forms. If you hardcode the same logic in each controller for each form (yes you should have a controller per form i.e. one per view) then you are repeating yourself multiple times. And writing tests that need to check the controller can do the basics (post data, manage changes to the view) but also test all of that of that logic for each controller.
Or instead write that logic once, put it in a service, write one test for it and inject it wherever you like.
References
Look at the Angular documentation, and look at the patterns that Angular implements and why these are good to follow (Design Patterns - the big ones being modular, dependency injection, factory and singleton).
The biggest problem with controllers is that you don't define the html it works on.
When you use...
<div ng-controller="myController"></div>
... then you have to inject your html in your controller, which is basicly old fashioned jQuery thinking.
When you use...
<div ng-controller="myController">... some html ...</div>
... your directive and your html it works on are defined in different places. Also not what you want.
Using directives forces you to put your piece of html and the code that it needs in the same place. Because the directive also has it's own scope, there will not be any interference with other variables elsewhere in your code. If you need variables from elsewhere you also have to explicitly inject them, which is also a good thing.
The word I use for why this is a good thing is 'atomic' but I'm not sure if this is the right word. Meaning: all the things that should work together are in one file. With templateUrl this isn't exactly true anymore, still the template is defined in the directive.
So in my controllers there is no code that does anything with the dom. Just the bare minimum like some page/view counting code, or connecting API data to the scope, or doing something with $routeParam data. All other code is put in either Services/Factories (business logic) or Directives (dom logic).
BTW: it is possible to define a controller for your directive, but is normally only used for 'inter-directive communication' (so they can share state), but you only use this with directives that always work together (like a tab directive that is repeated inside a tabs directive).
The main reason why you don't write logic in controllers is all $scopes in the controller get garbage collected with $destroy() on route changes. In the ngView directive when a $routeChangeSuccess broadcast is received, there is a function that only keeps $scope for the currently active view, all other $scopes are destroyed.
So for example, if you have a shopping cart app and your business logic is the controller using $scopes, the user will lose the product and all form data already entered on the order page, if they use the back button, etc.
Been reading the Angular.js' Controller docs and stumbled across:
Sharing stateless or stateful code across Controllers — Use angular
services instead.
But this leaves me uncertain. How do one can share a stateless/stateful code between controllers? Or what does the "code" mean here? A model? Besides, controllers do not refer to each other, as far as I understood. Can anyone clear things out for me(others) please? Thanks.
I think what they are referring to might be one of the methods to "persist" data, sharing it between controllers or between route-changes. One way to do that is to put it in your rootScope, another is to use a service. If you define a service like this:
.factory("MyDataObject", function() {
return {};
})
Then MyDataObject will be the same object anywhere you call it, allowing you to save things into it in order to share data, functions and states between controllers (or directives, or other services, etc).
You never know with the Angular documentation, but I would guess that is what they are talking about :)
See for example this answer: Angularjs, passing scope between routes
Here is my view on subject. As angular guys have always tried to explain, scope is not your model. Angular "services" are way to do it, but word service is such and overloaded term. Coming from DDD background, I cannot reconcile word service with a state or statefulness, it just does not make sense to me. What makes more sense is ViewModel or whatever you want to call it. Since I've worked with Silverlight using MVVM pattern, I call them ViewModel. As it is a job of a "Controller" to provide Scope for a View, my controllers have been so far very lean. Bulk of logic is in a ViewModels that get associated with a View through a $scope that controller creates. Does that make sense? So my controller might take a dependency of let's say mySearchViewModel, bulk of the logic is in there and can be shared between controllers, and to associate it with a view you would do something like $scope.vm = mySearchViewModel in mySearchController.