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.
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.
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.
I'm really new on AngularJS and i don't know the best practices so here is my question.
What is the recommended use of parents controllers properties? Should I use different alias trough the entire webapp or there is another way to use vars and methods of a parent controller?
I'm using partials html and seems it can be confusing see userCtrl.doSomething when the partial has no declaration of userCtrl (because its declared on a partial thats includes this one).
Thanks in advance!
You should avoid to use $parent and scope variables that are not declared in your controller.
Why ? Because if you use it, your controller will be directly dependent to where you instantiated it (ng-controller). So you won't respect the MVC pattern. Your controllers should not be dependent on the View.
Your controller should not know what are his parents. Because you can do it doesn't means that you should to it.
To share data between controllers, use Services instead. That's easier and cleaner.
I just started to use AngularJS, so I'm not an expert.
I have a div that represent the right area of my html view. In that div I have a controller, i.e.
<div class="rightContainer" ng-controller="rightContainerCtrl">...</div>
Inside that div I have a table, a search region, etc. Each region inside that div has its own controllers, it looks like this:
<div class="rightContainer" ng-controller="rightContainerCtrl">
...
<div class="search" ng-controller="searchCtrl">...</div>
...
<div class="table" ng-controller="tableCtrl">...</div>
</div>
the search region for example has its own controller and it is a child of rightContainerCtrl because it needs to alter some content in the parent (rightContainerCtrl), but the rightContainer div is growing and now it's big, and contain several nested controllers.
I think that using this nested controllers it's bad in this context because all the nested controllers share the parent scope, and not all controllers needs to access all the parent scope variables, also all the controllers are "prisoners" of the rightContainerCtrl, so they are highly coupled with their parent controller.
It looks like a God object anti-pattern (God controller in this case), so I think that instead of using nested controllers I can refactor my code to eliminate the rightContainerCtrl controller and use a service instead (like in a facade design pattern), that service then will be used by the controllers instead of sharing scope variables.
but since I'm not an AngularJs expert I'm not sure if I'm right or if it's better to leave this parent controller, maybe I'm missing something, so my question is
When is better to use nested controllers (nested scopes) and when it's better to use services instead in angularjs?
Controller/scope hierarchy should not dictate how data/models are shared in an application. When you think of data sharing in Angular, think dependency injection.
In the video that is referenced in #shaunhusain's answser, Misko states that scope should refer to the model, not be the model -- so don't model/put your data into scopes. Your models/data should normally be in a service.
When writing an Angular app, first think about your models. Put them in services with APIs to get/edit/manipulate the models. Then design your views. Each view should project/use/manipulate some subset of your models. Define a controller for each view that simply glues the needed subset of the models to the view. Make your controllers as thin as possible.
(Naming a controller rightContainerCtrl is also not recommended. The controller should not know about presentation/layout.)
This is 100% judgement call, and should be based on a couple of points.
Using events creates extremely loosely coupled components, they literally do not need to be aware of one another, if you have a situation where some parent controller would alleviate the need to communicate between a bunch of controllers (via services) then it is probably a better solution.
However if you're okay with the controllers each depending on the service (not really a problem) then you could just use the service as a means of communicating changes between the controllers. I've seen tons of arguments against the singleton (of which the service is a flavor, an injected singleton, but singleton nonetheless) I find these arguments to mostly be moot and generally lack a truly elegant and concise solution. If an argument spouts on and on about how when you go from A - D you don't want to take road B but they never seem to offer road C I don't really see the point.
http://www.youtube.com/watch?v=ZhfUv0spHCY&t=30m34s
I couldn't find the exact point in the video, but somewhere at the end here he discusses the usage of controllers vs services (he also reviews bi-directional data binding which will stop you from needing to pollute the global scope so to speak).