I know this question is going to be a bit debatable but I find this could actually be helpful to developers looking to do big projects with angularJS.
Best practices (according to John Papa at least) say that it's good to encapsulate angularjs controllers in an unanimous function and inject the providers/services/factories in them. So basically you would end up with something like this:
(function() {
"use strict";
angular
.module("app.components")
.controller("ctrl", ctrl);
ctrl.$inject = ["$scope", "someFactory", "someOtherFactory"];
function ctrl($scope, someFactory, someOtherFactory) {
// controller logic
}
});
If you work on a big project the most reasonable assumption is that you will want to have as less redundancy as possible so I ended up with having factories for each class of api endpoints from the server so I have them grouped by server scope.
In a complex screen, I will use maybe five to six of those and so you can imagine the inject starts to get long.
What I would want to do is have a "#data" that I can inject and through that #data I want to access the factories containing the $http calls.
My feeling is that this #data should not be a file containing all the configurations for the factories it redirects to but more like a provider where you can register with a configuration method the factory someting like this:
(function() {
"use strict";
angular
.module("app.factory")
.factory("someFactory", someFactory);
someFactory.$inject = ["$http"];
function someFactory($http) {
// factory logic
}
angular
.module("app")
.config(someFactoryConfig);
someFactoryConfig.$inject = ["#dataProvider"];
function someFactoryConfig(#dataProvider) {
#dataProvider.register("someFactory");
}
});
This would be similar to how you define a state inside the ui-router.
Any ideas on how to implement the provider?
I know this might not answer your question but Angular 2 tries to go away from Controllers and $scope.
Instead when you start your project you should plan it into features and from these features create different modules. Within these modules you will have all of your directives (components), services & factories to support the whole module.
This way you encapsulate your application into smaller applications. This will ensure that if 1 part of your application does not work the rest of your application will not be affected.
Angular can be a hard nut to crack and gets a lot of flame for that. But if you plan your project correctly Angular can be one of the most powerful frameworks out there.
Related
I went deep into investigating of modern approaches and stuck with a problem.
One approach is to split application modules depending on purpose:
angular.module('controllers', [])
angular.module('directives', [])
angular.module('services', [])
Another approach is to split modules by feature
angular.module('page.user', [])
.controller()
.service()
I do like the second one and believe that is the most efficient one. BUT, what about reusable services? Here is a brief example I took from my head:
angular
.module('myApp') // myApp refers to the main app module
.service('userService', function($http) {
let self = this;
self.getUser = function(id) {
return $http.get('api/user/' + id)
.then(functions() {
// some important logic hoes here
})
}
})
So I have a getUser method, that contains some specific business logic.
I do not want copy the same method to every new feature-module because DRY. I want every feature, that need getUser ,method to refer userService, so in this case I would need to put the service into main app module which will bootstrap all other feature-modules as well, or I need to create a modules specially for shared services and now we at the first on purpose approach.
So the question is more like how to stick to the second approach and keep everything well organized with DRY principle. Thanks
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.
I started learning angularjs and I see 2 types of module uses and I cant understand when to use this and when this:
example1:
var app = angular.module('app', []);
app.factory('Comment', function($http) {
}
app.controller('mainController', function($scope, $http, Comment) {
}
or
example2:
angular.module('commentService', [])
.factory('Comment', function($http) {
}
angular.module('mainCtrl', [])
.controller('mainController', function($scope, $http, Comment) {
}
var app = angular.module('app', ['commentService','mainCtrl']);
sometimes I see module declarion in each file(services factory controllers and so on)
and sometimes I see using the app module in those files, what is the right way? and why both of them works?
First and foremost, if you're concerned about app organization, read the discussion on the yeoman-generator-angular issue board from about a year ago. There's way more wisdom there than you could feasibly expect to get in a SO answer.That said, I'll try to provide an answer that's more pertinent to your specific concern, and (marginally) more concise.
DO NOT make a module for a single controller like angular.module('MainCtrl',[]).controller('MainCtrl'), or even a set of controllers who are only related in that they are all controllers:
angular.module('appCtrl', [])
.controller('appleController', function ($scope) {})
.controller('orangeController', function ($scope) {})
.controller('unicornController', function ($scope) {})
;
Why is this a bad idea? First, by definition, it's categorical, not modular; modules are groupings of system components which are related topically, not conceptually. When you're building a car, you don't put in all of the nuts and bolts at once. You put in the nuts and bolts that hold together the module that you're building. Controllers are like nuts and bolts. An engine is like a module.
Second, now you have to inject the appCtrl module anywhere that you want access to a controller. That's just a mess for developers to have to deal with; they're always digging through the code trying to find "whatever that module with that one thing in it" was, or they'll just repeat code all over the place.
Dependency Injection in AngularJS is less a rule than a (clever and awesome) string manipulation hack, and JavaScript has no "namespacing" system in the classical sense. Creating modules like app.products or app.cart is more for the developer's convenience and/or controlling the release cycle than making the program "work".
For these and other reasons, I caution developers against "Premature Modularization". If you're writing something app-specific---that is, you won't be reusing it right now in another app---why not just attach it to your app module? Then you have access to it anywhere in your app that you want it. (There are of course complexities that might cause you to change this, but if/when those do arise that's when you modularize).
Structure your directories by feature (Angular conventions do condone BDD, after all):
|-app/
|-|-cart/
|-|-|-CartItemsModel.js
|-|-|-OrderRepository.js
|-|-|-cart.html
|-|-|-add-item-modal/
|-##some more cart stuff
|-|-checkout/
|-|-|-confirmation/
|-|-|-|-confirmation.html
|-|-|-|-confirmation.less
If you're writing feature files or getting acceptance criteria from your employer, you can even model your directory structure directly after those features or requirement sets. That makes everything smooth from defining the scope of an iteration through documentation through revisiting code at a later time.
Both example work. But for instance with the example 2, this syntax can be used for separation of concern. You may need a module that is responsible for providing services which gives you function for Rest calls. Or another module that package some directives.
It's generally considered good practice to segregate your modules into different files. This will ensure that you are practicing good code control, and gives your code a greater degree of portability and debug-ability. Instead of declaring modules in the same file, declare them in different files with their module name.
It's good practice to segregate your files based on their module-controller relationship -- that is, each controller should have its own file, like this:
\scripts\controllers\
- - main.js
- - comments.js
Then, inside your main.js file, you'll start off with the controller declaration:
//main.js
angular.module('app')
.controller('MainController', function ($scope, $http) {
// Controller-scope stuff here
});
Finally, inside your comments.js file:
angular.module('app')
.controller('CommentsController', function($scope, $http, $routeParams) {
// Comment controller code
});
One way to help with organization right off the bat is to organize your files is with a seed template, like Angular Seed.
Just remember that you want to keep your module('app').controller('whatever') inside whatever.js, and any other .factory('forWhatever') (like in your example) should be contained with that parent .controller('whatever') inside the whatever.js file.
I have an existing angular project that has something really weird with the controller. It looks like the following.
app.controller('AppController', ['$scope', function ($scope) {
var app = app_application;
angular.extend($scope, app);
$scope.itTransports = app.state.itTransports;
}]);
I have a proof of concept for something I am trying to do on this fiddle and an attempt to mix the above controller and my concept in this fiddle but I cant seem to get it to work. I think it is something to do with the weird way the above controller works but I cant break it too badly and cant talk to the previous developer. I would think that if a combination cant be done then I would need some way for one controller to call another one but I want to make sure before I go down that path.
Edit: My main goal is to add functionality to check if the cookie exists. I am trying to do this in the same controller just for simplicity sake, but like I said before I am not adding a new one.
If you have some common functionality that you want to access from multiple controllers (e.g. checking if a cookie exists) then you should put that functionality into a service and inject that service into both controllers.