I inherited some code that is using a global object to to store angular services. These services get attached to the global object in the run function of the angular module. My question is, can this lead to trouble down the road? What sort of trouble does this cause for testing? Passing around services like this seems a lot easier than injecting all of the services in each controller so I see why this is done. What are other arguments for not doing this? Here is some code to illustrate what I am talking about:
// vars
var globalObject =
{
ng: {},
};
// Setup module
var myModule = angular.module("myModule", []);
myModule.config(doStuff);
myModule.run(setUpGlobals);
// Setup app globals
function setUpGlobals(ngRootScope, ngHttp, ngTimeout)
{
globalObject.rootScope = ngRootScope;
// angular services
globalObject.ng.http = ngHttp;
globalObject.ng.Timeout = ngTimeout;
}
setUpGlobals.$inject = ['$rootScope', '$http', '$timeout'];
Modules and DI were introduced in Angular exactly to avoid relying on globals and improve modularity.
This is naive approach that only works if there is a single module and a single application instance. It will fail if there are several modules that can be used separately (including tests). It will produce awful bugs if there is more than one application instance on the page (for example, if Angular is used for non-SPA applications).
A monolith module hurts testability. Even if it is used like that, some options will be unavailable, e.g. injecting spied or stubbed services in $controller(...) - because a controller relies on globals.
setUpGlobals results in eager service instantiation. This may not be a problem for core services but will be a problem for services that don't need to be instantiated right now.
Less important concern is code size in minified application. ng.$rootScope can be minified to a.$rootScope but not any further. Annotated function should mention '$rootScope' string once, but $rootScope variable name can be minified to a. There will be improvements if a service is used more than once inside a function.
There's a lot of reasons why global variables are bad. Some of them won't be applicable in this case, other ones will.
This makes testing nightmaraish. Dependency Injection is great, because it means you can do some atomic tests, by mocking out the services you don't need. In a simple, non-convulted example, imagine a service that makes API calls via http, if you DI it in, your test can mock out the http and just fake a return, letting you test only the bits of code you want, and saving you from having a test that relies on the API, or worse a test suit that uses up your API calls. With the provider in the global scope, that's much more difficult to achieve.
Just one reason, i'm sure there are many others.
Related
I am working on an AngularJS application that has some self-registering components. Concretely, there are some services that do not directly offer any public interface themselves; they merely register some internal objects in some directory provided by another service.
You can imagine the body of such a service's factory as follows:
var internalThing = {
// members ...
};
thingRegistryService.registerThing(internalThing);
return {};
Thus, I only need to ensure that the service gets loaded at some point.
The dilemma I'm facing is as follows: As the service provides no public functions and just needs to "be there", there is no reason to inject it anywhere. As it does not get injected, it never gets instantiated. As it never gets instantiated, the components within the service never register themselves, though.
I can inject the service the usual where in some service or controller that I know will get loaded - but then, I am basically leaving an unused argument in the code (which, if it is the last argument in the list, will even get outlined as an error based on the project's JSHint settings).
Alternatively, I can do the self-registration in a method in the service and call that wherever I inject the service. This would make the service injection "useful", but in turn I'd have to deal with multiple calls myself instead of relying on the built-in singleton mechanism of AngularJS's services.
Or ... should I go yet another route, by providing a method like registerThing somewhere that takes the service name as a string, and that will internally just invoke $injector.get? Of course, that evokes the question again where the correct place to put that kind of call would be.
A little background: This is part of a large project developed in a large team. Build and/or deployment magic somehow handles that any JavaScript code file committed to our VCS by any developer will be available to AngularJS's dependency injection. Thus, any JavaScript that needs to be added has to be provided as some kind of an AngularJS service, controller, etc.
What is the proper way to load such a self-registering service?
Right place to init your module is angular.module('yourModule').run block.
In case of "self-registering" I think it is better to have some implicit method for this:
angular.module('yourModule').run(['service'], function (service) {
service.init();
})
If your build system magically provides all JS code, is an import needed?
A self-registering architecture as your build system suggests should not require imports. The import JSHint errors are the cost of this magic.
I've used a similar behavior with namespace-like design. It can be used for self-registering techniques, although imports get tricky and does not work well with ES6 modules.
This is close to my point: http://blog.assaf.co/automating-component-registration-in-angularjs/
Does it make sense to use angular-services when we use ES6 modules? For example we need a singleton module (userService) in our code and we can do like this:
var app = angular.module('app', []);
app.service('userService', function(){
this.users = ['John', 'James', 'Jake'];
});
app.controller('FooController', ['$scope', 'userService', function($scope, userService){
console.log(userService);
}]);
But we can define the service in separate file:
/* ./user-service.js */
export default users = ['John', 'James', 'Jake'];
, then make the code like this:
var app = angular.module('app', []);
var userService = require('./user-service')
app.controller('FooController', ['$scope', function($scope){
console.log(userService);
}]);
and result will be absolutely the same as with services using. So why use angular services when we can use modules?
Yes! It makes perfect sense.
Services implement a particular responsibility in your application, moving data between the data store and views.
Modules allow you to organize your code and separate sections with different responsibilities.
By putting each service into a module, you make it easier to browse and test your code. It's easy to find all of the code that implements a responsibility.
Source: Difference between service, directive and module =)
From my own personal notes (mostly snippets from the docs, google group posts, and SO posts):
Modules
provide a way to namespace/group services, directives, filters, configuration information and initialization code
help avoid global variables
are used to configure the $injector, allowing the things defined by the module (or the whole module itself) to be injected elsewhere (Dependency Injection stuff)
Angular modules are not related to CommonJS or Require.js. As opposed to AMD or Require.js modules, Angular modules don't try to solve the problem of script load ordering or lazy script fetching. These goals are orthogonal and both module systems can live side by side and fulfill their goals (so the docs claim).
Services
are singletons, so there is only one instance of each service you define. As singletons, they are not affected by scopes, and hence can be accessed by (shared with) multiple views/controllers/directives/other services
You can (and probably should) create custom services when
two or more things need access to the same data (don't use root scope) or you just want to neatly encapsulate your data
you want to encapsulate interactions with, say, a web server (extend $resource or $http in your service)
Built-in services start with '$'.
To use a service, dependency injection is required on the dependent (e.g., on the controller, or another service, or a directive).
Directives (some of the items below say essentially the same thing, but I've found that sometimes a slightly different wording helps a lot)
are responsible for updating the DOM when the state of the model changes
extend HTML vocabulary = teach HTML new tricks. Angular comes with a built in set of directives (e.g., ng-* stuff) which are useful for building web applications but you can add your own such that HTML can be turned into a declarative Domain Specific Language (DSL). E.g., the <tabs> and <pane> elements on the Angular home page demo "Creating Components".
Non-obvious built-in directives (because they don't start with "ng"): a, form, input, script, select, textarea. Under Angular, these all do more than normal!
Directives allow you to "componentize HTML". Directives are often better than ng-include. E.g., when you start writing lots of HTML with mainly data-binding, refactor that HTML into (reusable) directives.
The Angular compiler allows you to attach behavior to any HTML element or attribute and even create new HTML elements or attributes with custom behavior. Angular calls these behavior extensions directives.
When you boil it all down, a directive is just a function which executes when the Angular compiler encounters it in the DOM.
A directive is a behavior or DOM transformation which is triggered by a presence of an attribute, an element name, a class name, or a name in a comment. Directive is a behavior which should be triggered when specific HTML constructs are encountered in the (HTML) compilation process. The directives can be placed in element names, attributes, class names, as well as comments.
Most directives are restricted to attribute only. E.g., DoubleClick only uses custom attribute directives.
see also What is an angularjs directive?
Define and group Angular things (dependency injection stuff) in modules.
Share data and wrap web server interaction in services.
Extend HTML and do DOM manipulation in directives.
And make Controllers as "thin" as possible.
While am looking into the FEQs of Angularjs I've seen below article:
$rootScope exists, but it can be used for evil
Scopes in Angular form a hierarchy, prototypally inheriting from a root scope at the top of the tree. Usually this can be ignored, since most views have a controller, and therefore a scope, of their own.
Occasionally there are pieces of data that you want to make global to the whole app. For these, you can inject $rootScope and set values on it like any other scope. Since the scopes inherit from the root scope, these values will be available to the expressions attached to directives like ng-show just like values on your local $scope.
Of course, global state sucks and you should use $rootScope sparingly, like you would (hopefully) use with global variables in any language. In particular, don't use it for code, only data. If you're tempted to put a function on $rootScope, it's almost always better to put it in a service that can be injected where it's needed, and more easily tested.
Conversely, don't create a service whose only purpose in life is to store and return bits of data.
— AngularJS FAQ - $rootScope exists, but it can be used for evil
So My doubt is why $rootScope is not recommended for functions as a global function? Is there any performance issue?
I've answered this in the past, but it's good that you're asking these questions.
$rootScope exists, but it can be used for evil Scopes in Angular form a hierarchy, prototypally inheriting from a root scope at the top of the tree. Usually this can be ignored, since most views have a controller, and therefore a scope, of their own.
Non-isolated scopes are hierarchical, but most developers should be using directives that have isolated scopes. The very hierarchical nature of AngularJS's scope is the source of many bugs in angular apps. It's a problem I like to call scope bleeding where a scope property is modified magically somewhere in the DOM tree and you don't know why.
Angular's default behavior is to inherent scopes and this makes it tempting for one controller to update something managed by another controller, so on, and so on. This is how spaghetti connections between source code is created. Making it very difficult to maintain that code.
Occasionally there are pieces of data that you want to make global to the whole app. For these, you can inject $rootScope and set values on it like any other scope.
No that's not correct. AngularJS allows you to define things like constants, values, and services. These are things that can be injected into routes, controllers and directives. That is how you make things accessible globally to your app, and this how you do it if you want to make your controllers or directives testable. A unit test writer doesn't know what properties should be in the $rootScope that a directive or controller depends upon. They have to assume that the $rootScope has not mutated to provide a service or data.
Of course, global state sucks and you should use $rootScope sparingly, like you would (hopefully) use with global variables in any language.
The problem isn't $rootScope but what people are doing with it. Many apps add the current user, the auth tokens, and the session data into the rootScope. This ends up getting used heavily in templates (shows X if user logged in otherwise show Y). The problem is that the HTML doesn't communicate scope hierarchy. So when you see {{user.firstname + ' ' + user.lastname}} you have no idea where the variable user came from. The second problem is child scopes can shadow root properties. As in the previous example if a directive does this scope.user = 'bla bla bla'. It hasn't replaced the value on the rootScope. It's hidden it. Now you get some strange unexpected things in the templates, and you don't know why the variable user has changed.
Conversely, don't create a service whose only purpose in life is to store and return bits of data.
Angular's $cacheFactory and $templateCache are examples of services that exist only too store data. I think the author was trying to encourage the use of constants and values in Angular's modules, but that's not a good description to do that.
So My doubt is why $rootScope is not recommended for functions as a global function? Is there any performance issue?
The $rootScope is the only scope available during angular.config(..). It's during this time that the scope can be modified if this is the only time that you can do it. For example; you may need to inject an API key or Google anayltics variable before the app starts.
Functions on any scope are generally a bad idea. Mainly for the reason that everything in scopes is digested in expressions on the templates. Functions tent to hide heavy operations. It's impossible to tell how heavy a template is by reading the HTML when it calls a function. I've seen scope functions like getHeight() where the function itself performed 3 levels of nested loops. That function has to get called every time angular digests the watchers to see if it's changed. You should try to keep your templates as dry as possible.
Global Variables are Abused
$rootScope is pretty much a global variable and has its place but is definitely abused by most of the people that use it. These are the reasons Globals in general should not be used.
Non-locality -- Source code is easiest to understand when the scope of its individual elements are limited. Global variables can be read or modified by any part of the program, making it difficult to remember or reason about every possible use.
No Access Control or Constraint Checking -- A global variable can be get or set by any part of the program, and any rules regarding its use can be easily broken or forgotten. (In other words, get/set accessors are generally preferable over direct data access, and this is even more so for global data.) By extension, the lack of access control greatly hinders achieving security in situations where you may wish to run untrusted code (such as working with 3rd party plugins).
Implicit coupling -- A program with many global variables often has tight couplings between some of those variables, and couplings between variables and functions. Grouping coupled items into cohesive units usually leads to better programs.
Concurrency issues -- if globals can be accessed by multiple threads of execution, synchronization is necessary (and too-often neglected). When dynamically linking modules with globals, the composed system might not be thread-safe even if the two independent modules tested in dozens of different contexts were safe.
Namespace pollution -- Global names are available everywhere. You may unknowingly end up using a global when you think you are using a local (by misspelling or forgetting to declare the local) or vice versa. Also, if you ever have to link together modules that have the same global variable names, if you are lucky, you will get linking errors. If you are unlucky, the linker will simply treat all uses of the same name as the same object.
Memory allocation issues -- Some environments have memory allocation schemes that make allocation of globals tricky. This is especially true in languages where "constructors" have side-effects other than allocation (because, in that case, you can express unsafe situations where two globals mutually depend on one another). Also, when dynamically linking modules, it can be unclear whether different libraries have their own instances of globals or whether the globals are shared.
Testing and Confinement - source that utilizes globals is somewhat more difficult to test because one cannot readily set up a 'clean' environment between runs. More generally, source that utilizes global services of any sort (e.g. reading and writing files or databases) that aren't explicitly provided to that source is difficult to test for the same reason. For communicating systems, the ability to test system invariants may require running more than one 'copy' of a system simultaneously, which is greatly hindered by any use of shared services - including global memory - that are not provided for sharing as part of the test.
Source: http://c2.com/cgi/wiki?GlobalVariablesAreBad
Sharing data in Angular
When it comes to sharing data across controllers in Angular you should use a service. With your custom service you can create a getter and a setter method. You inject it to the controllers you need it and can use it in your app.
There exist no performance issues. It would actually boost your performance by a fraction of time, because you dont need to dependency-inject a lot of services.
But it's a big concern of design. Consider a large application with dozens and dozens of views, complex components and tied to a number of well known APIs (e.g. Twitter, Flickr, Facebook, OAuth, ...).
You wont develop this application alone. The following issues will arise:
Namespacing
You are working on the Facebook API, someone else is working on the Twitter API. You both think that using $rootScope for functions is a good idea and both write a $rootScope.login function. How would you resolve this when doing git merge? You need namespacing, alas, you need to develop two services myFacebookAPI, myTwitterAPI which then can implement the same interface for loggin in. (login(user,pw)). Note that this gives you the ability to abstract away the actual social network you are dealing with in the controller, when you can do something like:
$scope.callAction = function (action) {
var service;
if ($scope.serviceSelected === 'fb') {
service = myFacebookAPI;
} else {
service = myTwitterAPI;
}
service[action]();
};
Testing
When developing professionally, you write tests. Angular gives you tools to do automated tests for services, etc., but you wont be able to test something you assign to $rootScope in the same comfortable manner.
Other issues will arise too, but i think this should be enough for you to think about it on your own.
Ok so I was reading here
basically when I have this
MyApp = MyApp || {};
MyApp.settings = {
isFooEnabled: false
}
if I use the rootscope and want to check if isFooEnabled I have to inject the rootScope into whatever object I want to do the check.
How does that make sense?
What is the advantage of using $rootScope.isFooEnabled over using straight standard javascript MyApp.isFooEnabled?
what is better for what?
when should I use one over the other?
The $rootScope is the top-most scope. An app can have only one $rootScope which will be shared among all the components of an app. Hence it acts like a global variable. All other $scopes are children of the $rootScope.
The rootScope's variable is set when the module initializes, and then each of the inherited scope's get their own copy which can be set independently.
NOTE:
When you use ng-model with $rootScope objects then AngularJS updates those objects under a specific $scope of a controller but not
at global level $rootScope.
The $rootScope shouldn't be used to share variables when we have things like services and factories.
Finally, Angular FAQ says this at the bottom of the page: "Conversely, don't create a service whose only purpose in life is to store and return bits of data." See from here.
Actually, I would argue that you shouldn't use $rootScope in this case, you should create a separate service (or factory) that stores your settings, however, the usage and reasons are the same.
For simply storing values, the primary reason is consistency. Modules and dependency injection are a big part of angular to ensure you write testable code, and these all use dependency injection so that unit tests can be written easily (dependencies can be mocked). Whilst there are not many obvious gains from injecting a simple object, it's consistent with the way more complex code is accessed, and there is a lot to be said for that. On a similar note, if you were to upgrade your settings object to fetch data from the server (e.g. for environment specific settings), you might want to start unit testing that functionality, which you can't really do properly without modularising it.
There is also the (frankly weak) namespacing argument - what if another library you import uses window.MyApp?
TL;DR: It's a strongly recommended best-practice. It might seem a bit clunky now, but you'll benefit from doing it in the long run.
As I'm parsing through AngularJS documentation, I thought I would post this to get some other opinions.
My specific scenario is that I have settings I wish to change on an injectable using the run method of my module. I have a couple of different ways I can access the injectable and wasn't sure if there was a discernible functional advantage to using one over the other. Or do they boil down to essentially the same thing.
Say for instance my module is defined as thus:
var app = angular.module('MyModule', ['some.Third.Party.Module']);
Now consider that there is a factory in the third party module with variable that needs to be set. This can be accomplished by doing the following:
app.run(['some.Third.Party.Module.Factory', function (theFactory) {
theFactory.someVariable = 'foo';
}]);
An alternative method would be:
app.run(function ($injector) {
var theFactory = $injector.get('some.Third.Party.Module.Factory');
theFactory.someVariable = 'foo';
});
Is one method better than the other? Maybe there is a third option I haven't considered yet?
You are actually using the three different methods (to my knowledge) Angular provides for injecting dependencies.
Angular can identify a dependency solely through the name of a function parameter. You are doing this here when you inject $injector.
app.run(function ($injector) { ... });
The above is quick and easy for development but you can run into problems when minifying code as variable names can change.
For production, and generally a good practice in development is to use annotations as you already have in your second code example for some.Third.Party.Module.Factory.
app.run(['$injector', function ($injector) { ... }]);
The third way I know of is to use $injector directly.
This is useful for unit testing and if perhaps you wanted to conditionally switch up which dependency is injected.
It provides dynamic annotation rather than static.
The former is effectively shorthand for the latter, but keep in mind that referencing $injector directly will cause problems if you minify the code since that variable will be renamed. Your first usage is minifaction-proof.
I would generally stick with the first usage, but you could also use the $inject annotation like so:
var MyController = function($scope, greeter) {
// ...
}
MyController.$inject = ['$scope', 'greeter'];
someModule.controller('MyController', MyController);
This makes sense if you want to add properties to MyController's prototype and still use it as the controller definition.
I'm really not sure of a scenario where you would need to use the $injector directly unless you were doing something very fancy.
Also keep in mind the difference between the run and config methods. The latter lets you use providers which allow you to configure services used in the run method (and when they are injected).
I think there is two cases when you need to get service/factory/whatever via $injector:
1) you have circular dependency, but you sure that everything will be fine.
2)you need to get something from "angular-world" from outside of it.
upd: also case provided by #Explosion Pills looks interesting.