When to use $injector in AngularJS - javascript

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.

Related

Avoiding angular dependency injection with use of global object

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.

Why would you ever use the $rootScope?

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.

Why wouldn't you use explicit annotations when defining controllers in AngularJS?

I am new to AngularJS and learning about the two styles of writing controller functions. It seems as though the only reason someone would not use explicit annotations is to save time, which doesn't seem like a good reason. And being able to minify/obfuscate code seems like a requirement I would want to keep in any application.
Also note that I am not asking which is better or asking for a debate. I am asking for what reasons (or in what situation) it would be more beneficial to not use explicit annotations.
Example of what I'm talking about:
module('myApp').controller('MyController', function($scope) {});
vs.
module('myApp').controller('MyController', ['$scope', function($scope) {}]);
The inline array annotation is simply a workaround over Javascript limitations to allow Angular code to be minified and not to stop working. But it isn't a great solution because if forces you to duplicate your code. And we all know how bad duplicate code is. Angular documentation itself acknowledges that:
When using this type of annotation, take care to keep the annotation
array in sync with the parameters in the function declaration.
It's way too easy to add a new dependency and forget to add the corresponding annotation. Or to reorder the arguments and forget to update the list of annotations. Trust me. Been there, done that.
Fortunately there are tools developed by smart people that take that burden off our shoulders by taking care of annotating the code automatically. Probably the most known is ng-annotate, as mentioned by #pankajparkar. All you have to do is plug it into your build process and your code will be correctly annotated.
To be honest, I find it really odd that Angular documentation recommends against following this approach.
Obviously You need Array annotation of DI while doing JS minification,
If you don't want minification of JS files then you can continue
function pattern.
Refer this Article which has good explanation about what you want.
If you don't want to include array annotation of Dependency injection then simply you could use ng-annotate library. (As you are saying, its not bad pattern thought you can avoid it by ng-annotate)
Which does do convert your code to array annotation of DI while minifying js files
Only you need to add ng-strict-di attribute where ever you declared your ng-app directive.
<div ng-app="myApp" ng-strict-di>
I suppose the one situation where it would be useful and indeed necessary to use the annotations would be if you didn't want anyone to minify your application code.
Using direct arguments
when you pass argument to controller function, In this mechanism, you will pass arguments and angular will recognize
Ex.
module('myApp').controller('MyController', function($scope) {});
module('myApp').controller('MyController', function($scope,$http) {});
OR
module('myApp').controller('MyController', function($http,$scope) {});
In the second and third example, place of $scope and $http is different, but they work without error. This means angular recognizes which is $scope and which is $http.
Now consider you developed an web application and you are going to deploy it. Before deploy will minify your javascript code to reduce size of your js file.
In minification process, each variable will get shorten as like $scope may become a, and angular can not recognize 'a'.
Using array method
This is the standard mechanism, when you pass an array , the array elements are strings except last element, the last array element is your controller function.
You must need to specify the order of each argument in array as string and you can pass that variable in function, here you can provide any variable names as they are just alias.
ex.
module('myApp').controller('MyController', ['$scope',function($scope) {}]);
module('myApp').controller('MyController', ['$scope','$http',function(myscope,newhttp) {}]);
Now in this mechanism, if you use any minifier to minify this js, it will minify and change myscope to any other name say 'b', but it will not touch $scope as it is a string, and you dont need to worry as b is just a alias for $scope.
This is recommended, but if you are using grunt/gulp for minification, you can check these
ng-di-annotate
ng-annotate
The only real reason is that it's faster when mocking an application.
This is a leftover from early on in Angular, when they had a few features that they used for 'showing off' how easy it was to make an Angular app. Another example is how Angular used to look for controllers that were declared globally on window; they removed that 'feature' in 1.3. This changelog best explains why.
It's a fun little gimmick, and it helps ease new developers into the world of Angular, but there's no good reason to use it in a production app.

Is there any harm in adding more dependencies in angular js controller or service

Suppose this is my angular controller
app.controller("MyCtrl", function($scope, $modal, $state,) {
});
I am thinking of having one global variable holding most commonly used dependencies like
var all = ['$scope', '$modal', '$state']
and then use all at every place along with some other dependencies if needed
Is there any performace issue having put all dependencies everywhere
Having to inject more code would have a performance hit, but not a major one. I don't recommend defining your dependencies globally like that because dependencies should be very visible. You should know exactly what you're doing with them without having to open another file and check.
If you need to reuse a set of dependencies everywhere, that suggests more that there's probably something wrong with the code. How come different regions of the code base all talk to the same stuff? That suggests duplication of concerns. I don't extend that assertion to just having to inject $scope or $http all the time.
In short, I don't think it's a good idea to manage dependencies like that.
While I don't have deep knowledge of the Angular internals, nor have I bothered to actually measure the performance of what you are asking, I would venture my educated guess as .... no. You're not going to see a performance impact here. The only impact would be on controller instantiation, which only happens once per view. And even then, we're just talking about new-ing up a few objects ... the perf impact ought to be very negligible, and not something I would worry about.
You can't inject using a variable you define within another controller or service. One thing you could do is create a factory and put your dependencies in the $rootScope.
app.factory('root',function($rootScope, $modal, $state){
$rootScope.modal = $modal;
$rootScope.state = $state;
});
You just put $rootScope in all your controllers and you have access to whatever you like. You would only need to inject 'root' in your main controller (if you have one). It seems like fishy architecture to need this shortcut, but that is how I would do it. No perf hit really - non-primitives are reference types.

Using the $injector instance directly vs. obtaining dependencies as parameters

Within Angular, is it considered a good practice getting the dependencies directly from an $injector instance, rather than as parameters?
I have the problem that my controllers started having a lot of dependencies, so rather than:
myApp.controller(['$scope', 'Dep1', 'Dep2', 'Dep3', function($scope, Dep1, Dep2, Dep3) {
...
}]);
I would do:
myApp.controller(['$scope', '$injector', function($scope, $injector) {
var Dep1 = $injector.get('Dep1');
var Dep2 = $injector.get('Dep2');
var Dep3 = $injector.get('Dep3');
}]);
I find it the same functionality-wise, but with much less clutter on the parameters. I suppose that this will make my components slightly less easy to test though, right?
What do you think?
Based on my opinion and after reading the following posts:
Post 1
Post 2
Angular's documentation on DI (Dependency Injection)
Your second approach in order to minimize the long dependency list is considered as Service Locator Anti Pattern.
See - Service locator AntiPattern
Using the Service Locator Anti Pattern is bad because it will make your life as a maintenance developer worse because you will need to use considerable amounts of brain power to grasp the implications of every change you make. Using the $injector obfuscates the actual dependencies of the resource (in this case controller) and kills maintainability.
In addition, according to angular's documentation the the preferable way is:
Using the inline array annotation (preferred)
If your controller is ending up using so many dependencies maybe you are doing something wrong, Maybe you broke the Single responsibility principle. consider:
Delegate more of the logic to service(s) that are injected in
Separate out into different controllers, so each only has (just about) 1 responsibility
It depends how you write the code and how is easy to you and make you flexible to write code. I always write the controllers in this way hope it will help :}
var appCtrl = function($scope, others){...}
app.controller('appCtrl',['$scope', 'others', appCtrl]);

Categories

Resources