I would like to know if there is a difference between the two next lines and why to use one of those (the two work as expected)
phonecatApp.controller('PhoneListCtrl', function($scope, $http) {...});
phonecatApp.controller('PhoneListCtrl', ['$scope', '$http', function($scope, $http) {...}]);
I took it from the official AngularJS tutorial and I know there is an explanation about this modification but I don't understand it...
http://docs.angularjs.org/tutorial/step_05
Thanks in advance!
If you minify your first line you get:
phonecatApp.controller("PhoneListCtrl",function(e,t){})
The dependency injection won't work then, because Angular has no idea what e and t are. Compare that to minifying the second version:
phonecatApp.controller("PhoneListCtrl",["$scope","$http",function(e,t){}])
The function parameters are still renamed, but $scope and $http are given in the array so the injection can go ahead as expected.
There is no difference in terms of functionality. The first one may get messed up if your code is minified because angular resolves from the argument names. The latter has some kind of protection against minification because you are already passing dependencies in array.
AngularJS invokes certain functions (like service factories and controllers) via the injector. You need to annotate these functions so that the injector knows what services to inject into the function. There are three ways of annotating your code with service name information:
Using the inline array annotation (preferred)
Using the $inject property annotation
Implicitly from the function parameter names (has caveats)
For more information, see AngularJS Developer Guide - Dependency Injection
Related
I have seen controller being declared in two ways as below. But what diff does this make?
appmodule.controller('Actrl',['$scope',function($scope) {}]);
appmodule.controller('Actrl',function($scope) {});
But, most of the times, the 1st doesn't work. Why?
Both the syntax are same but the first one is preferred (there is a typo, see below description) if you are minifying your code.
Angular resolves the dependency based on the name so when you write appmodule.controller('Actrl',function($scope) {}); syntax, Angular injects the dependency of $scope by reading the argument name $scope. But when your code is minified for production level use then your code will become like:
appmodule.controller('Actrl', function(a) {});
Now, the Angular will not be able to resolve the dependency with the name a. That is why the first approach is used i.e. appmodule.controller('Actrl',['$scope', function($scope) {}]);. Now when your code is minimized for production, your code will be like this:
appmodule.controller('Actrl',['$scope', function(a) {}]);
Now, Angular can match the index based position that a is $scope.
There is a typo in your code where the list should not be closed before the function declaration.
Read the Dependency Annotation for more information on this under the topic Inline Array Annotation.
Angular invokes certain functions (like service factories and
controllers) via the injector. You need to annotate these functions so
that the injector knows what services to inject into the function.
There are three ways of annotating your code with service name
information:
Using the inline array annotation (preferred)
Using the $inject property annotation
Implicitly from the function parameter names (has caveats)
Edit:
Another more detailed description over the two different style: a-note-on-minification:
Since Angular infers the controller's dependencies from the names of
arguments to the controller's constructor function, if you were to
minify the JavaScript code for PhoneListCtrl controller, all of its
function arguments would be minified as well, and the dependency
injector would not be able to identify services correctly.
We can overcome this problem by annotating the function with the names
of the dependencies, provided as strings, which will not get minified.
There are two ways to provide these injection annotations:
[EDIT]
For your first case, this isn't the right syntax. The right one would be to encapsulate in the same array your dependency injection and your controller like the following:
appmodule.controller('Actrl',['$scope', function($scope) {}]);
The difference between both of your definitions is that in the first case you're explicitly specifying your injection dependencies. This will avoid to rename variables name during minification which would break your code. Hence the name in quotes [i.e. those strings] will be used in the minified versions.
Both approach are doing the same thing but the second one is just a syntactic sugar of the first one.
These are just two ways that AngularJS does Dependancy Injection. But this version,
appmodule.controller('Actrl',['$scope',function($scope) {}]);
in particular has been written to handle code minification. It is recommended use this version whenever possible.
To get the difference clear, you must first understand how AngualarJS does dependancy injection. For more details you can refer to:
Understanding Dependency Injection
The "Magic" behind AngularJS Dependency Injection
AngularJS Dependency Injection - Demystified
But to cut the long story short, AngularJS loops through each items by their names in the parameter list, looks up against a list of known names of objects that can be injected and then injects the objects if there is a match.
Let's have a look at an example:
appmodule.controller('myController',function($scope, $log) {
$log.info($scope);
});
Here, since $scope and $log (the order that you specify them in the parameter list doesn't matter here) are known objects to AngularJS, it injects them into myController. But if you were to do:
appmodule.controller('myController',function(someVar) {
// ...
});
AngularJS doesn't know about the parameter someVar and it throws a dependancy error.
Now let's come back to your example. Let me modify your 2nd version a bit:
appmodule.controller('Actrl',function($scope, $log) {
$log.info($scope);
});
If we use a minifier, let's see how this piece of code gets minified. I am using an online minifier for this purpose . After minification, it becomes:
appmodule.controller("Actrl",function(o,l){l.info(o)});
This is because, minifiers usually shorten the variable names to smallest size to save space. Notice how our $scope got renamed to o and $log to l.
Now, if we run this code, AngularJS doesn't know about o and l and it is going to cry about missing dependencies as we understood earlier.
AngularJS deals with this problem using the 1st version of Dependency Injection in your example. If it was:
appmodule.controller('Actrl',['$scope','$log', function($scope, $log) {
$log.info($scope);
}]);
After minification it becomes:
appmodule.controller('Actrl',['$scope','$log',function(o,l){l.info(o)}]);
Here, even though $scope and $log parameters were renamed to o and l respectively, the minifier didn't touch the strings '$scope' and '$log' and their order in the array.
Now when AngularJS injector sees this version using array, it substitutes each item in the parameter list in the function with the corresponding objects in the array (provided the objects are known to AngularJS).
So in our example, even after minification, AngularJS knows that it needs to substitute o with $scope and l with $log. Thus the code runs without any Dependancy Injection errors.
But one important thing is to note here is that, when we use this version the order of the items specified in the array and the parameter list of the function of really matters. That is, if you were to do:
appmodule.controller('Actrl',['$scope','$log', function($log, $scope) {
$log.info($scope);
}]);
, it is going to blow everything up!
What is the difference between these 2:
angular.module('myapp' ,[])
.controller('MyController', function($scope){...});
and
angular.module('myapp' ,[])
.controller('MyController, ['$scope', function($scope){...})];
This is quite complicated for those who new to AngularJS like me. The syntax is too different from Java and C.
Many thanks.
There's nothing difference between them. Both code works same way. But if you use first code and when you minify the code then it will confuse.
Look for an example:
.controller('MyController', function(a){...});//$scope is changed to a
And your code won't work as angularjs code uses $scope variable as it doesn't take first, second, third, and so on parameters.
So, the second code is safer than first as if when you minify the code, it still takes same variable i.e. $scope.
Look for an example:
.controller('MyController', ['$scope', function(a){...})];//a refers to $scope
So, the above code works fine when you minify the code as $scope is injected in place of a. So, if you pass multiple parameters then ordering matters in this example. Look at the following:
.controller('MyController', ['$scope','$timeout', function(s,t){...})]; s is injected as $scope and t is injected as $timeout. So if you change the order of them like ['$timeout','$scope', function(s,t){...})] then s is $timeout and t is $scope. So, ordering matters in this example but in your first example code ordering won't matter as name matters like $scope, $timeout.
There's also another way to inject variables if you use your first example code like below:
MyController.$inject = ['$scope'];
For multiple parameters,
MyController.$inject = ['$scope','$timeout'];
So, there are mainly three kinds of annotation:
Implicit Annotation - your first example code
$inject Property Annotation - the $inject method
Inline Array Annotation - your second example code
The second is minification safe.
Since Angular infers the controller's dependencies from the names of
arguments to the controller's constructor function, if you were to
minify the JavaScript code for PhoneListCtrl controller, all of its
function arguments would be minified as well, and the dependency
injector would not be able to identify services correctly.
Source
I'm kind of a big angularJS newbie and I'd like some highlights concerning dependency injection.
I've done some research and here is what I understand so far.
I have 2 service files (using factories) :
-mogService.js
angular.module('anglober.services').factory('mogService', ['$http', function($http) {
var mogService = {};
//mogService code here
return mogService;
}]);
-modalService.js
angular.module('anglober.services').factory('modalService', ['$modal',
function ($modal) {
//modalService code here
}]);
One controller file :
-mogCtrl.js
angular.module('anglober.controllers').controller('mogCtrl', ['$scope', 'mogService','modalService', function ($scope, mogService, modalService) {
//code using mogService and modalService parameters
}]);
As I understand it, the dependency injection is done by passing my services as parameters of the function parameter in my controller declaration, the array of string is here so that after minification, angular still knows which variable is what.
However, the modalService variable is undefined when I test my code. The mogService is recognized alright though (only if I remove any call to the modalService variable).
What am I doing wrong ?
I've read things about using $inject, which is the better practice and why ?
I'm declaring modules in the app.js as follows :
angular.module('anglober.services', ['ui.bootstrap']);
angular.module('anglober.controllers', []);
var app = angular.module('anglober', ['anglober.controllers', 'anglober.services', 'anglober.directives']);
Is this good practice ? Declaring modules and their respective dependencies in one file then only use "getters" without the dependencies array parameter in module files ?
Thanks for your time.
Three steps that work for me (plunkr):
1. Be sure you define a module's dependencies only once.
Indeed, check that angular.module('anglober.services', [...]); is indeed called only once with the second argument.
At the same time, be sure to call these lines before the actual services/controllers /... definitons.
2. Wire every dependency
You should add 'anglober.services' dependency to 'anglober.controllers': the last requires modalService which requires $modal, it may help angular anyway.
3. Add possible missing lib requirements, in the right order
First jQuery, then angular, bootstrap and eventually bootstrap.ui and your modules.
I've been reading through the tutorial, dev guide, and practicing on my own, but I'm having trouble piecing everything together in my mind with regard to dependency injection.
Question: Within the first code snippet in the linked page below, why is the name of the "service" located in front of $inject and why is the parameter of the service used here again? Or better yet what concepts am I lacking in understanding? I'd like to be able to piece it all together in my head step by step, but I'm still trying to understand how exactly even the globally defined "services/functions" can be written this way.
http://docs.angularjs.org/guide/dev_guide.services.understanding_services
So in that code snippet is injecting the $location service into MyController. So MyController depends on $location so it declares the dependency and its owns the dependency declaration.
Here is the code commented:
// declaring a Controller function
var MyController = function($location) { ... };
// $location service is required by MyController
MyController.$inject = ['$location'];
// Then register the Controller in the module.
// The module is the container that performs DI on the objects within it.
myModule.controller('MyController', MyController);
Typically though you'd do the following to declare the Controller and it's dependencies in one shot that's cleaner. The dependencies are strings at the front of the array before the final function which is the controller being registered. Here is the simpler definition:
myModule.controller('MyController', ['$scope', '$location', function($scope, $location) {
$scope.someFunction = function() {
// do something with $location service in here
};
}]);
Keep in mind this:
"...even the globally defined "services/functions"
The whole point of DI is to not define things globally because global definitions create coupling that make it hard to reuse major portions of your system (ie you can't break apart the system without instantiating the whole thing). Dependency Injection separates the dependency (ie MyController depends on/uses $location service) from where it finds that reference. Beginner developers, and some dense senior devs quite frankly, typically just define things globally and that's how everything gets a reference to their dependencies. DI allows code to simply declare its dependencies so its dependencies can be given to the code by an external entity instead of the code assuming where to get it from. Often this is called the Hollywood Principle - Don't call us we'll call you.
It looks like you're lacking a strong understanding of Dependency Injection in AngularJS.
Once you define a service, it then needs to be injected inside to the controller that is going to use it. The two code samples on that page show the two different methods of injecting the service into the controller.
I'd suggest you look at the docs: AngularJS: Dependency Injection
Angularjs has this nice feature of auto discovery of the providers based on a function arguments.
For example, if I want to use $http in some function i would call it like that:
$inject.invoke(function ($http) {
});
Angularjs will "know" what are my dependencies. It will figure it out by reading my function's body and based on argument names it will know.
However there is a problem when you would like to minify your code. Minifier will change arguments names. That's why we should use this notation:
$inject.invoke(['$http', function ($http) {}]);
or this notation:
function Foo ($http) {}
Foo.$inject = ['$http'];
$inject.invoke(Foo);
We should always in the end minify our code. So we should avoid using this magic (first example) notation.
And now my problem:
I'm trying to minify my js code and angularjs cannot resolve a provider name.
I can't find a place where i haven't specified .$inject = [...]. Now it just says: "Unknown provider a" and i don't know what function is it referring to.
Is it possible to turn off angularjs auto discover (auto-injector) of providers? I would test and repair my code before minifying.
So, I'm wondering how to disable this "magic" angularjs deduction.
Since I always minify my code I want angularjs to yell at me when I will accidentally use this superheroic evil.
How to turn it off?
Just edit the source. Find 'function annotate', and replace the fn == 'function' block with something like this:
if (typeof fn == 'function') {
console.log("Bad magic injection in "+fn.toString().replace(STRIP_COMMENTS, ''));
}
update:
If anyone needs this because of trying to minify, maybe here is another possible solution
ngmin. It is an AngularJS application minifier project.
Not sure if this help.
According to Igor Minar,
You should make something like this
factory('Phone', function($resource){ ... }))
to
factory('Phone', ['$resource', function($resource){ ... })])
Here is the official doc from Dev guide.
$inject Annotation
To allow the minifers to rename the function parameters and still be
able to inject right services the function needs to be annotate with
the $inject property. The $inject property is an array of service
names to inject.
var MyController = function(renamed$scope, renamedGreeter) {
...
}
MyController.$inject = ['$scope', 'greeter'];
Care must be taken that the $inject annotation is kept in sync with
the actual arguments in the function declaration.
This method of annotation is useful for controller declarations since
it assigns the annotation information with the function
From 1.3.0-beta.6 onwards angularjs supports ng-strict-di option which can be used along with ng-app directive to disable auto-injection.
From Documentation
if this attribute is present on the app element, the injector will be
created in "strict-di" mode. This means that the application will fail
to invoke functions which do not use explicit function annotation (and
are thus unsuitable for minification), as described in the Dependency
Injection guide, and useful debugging info will assist in tracking
down the root of these bugs