AngularJS Dependency Injection where to specify? - javascript

I am an angularJS noob here and, based on my understanding, there are two places where I can inject dependency.
angular.module('myApp', [A: HERE IS ONE PLACE TO DO IT])
.controller('HomeController', function(B: $hereIsAnotherPlace){
});
Am I right about this? If so, what are the differences?

In your example, A is where you can specify modules rather than dependency injection (DI). Below, addresses this variation of your code:
.controller('HomeController', [A , function(B) {}]);
The second one (B) is required, the first (A) is optional (but has benefits described below).
Here's an example of only using just the second (B) from the Angular docs:
function MyController($scope, greeter) {...}'
But Javascript minifiers and obfuscators can rename parameters and break that approach because angular expects, for instance, $scope to be named precisely $scope (and minifiers like to rename parameters to something as small as possible in order to shrink the file as small as possible).
One way, amongst others, around that is inline annotation:
someModule.factory('greeter', ['$window', function(renamed$window) {...}]);
(again from the angular docs). This gets around the issue as the minifers/.. won't change a string literal. And angular knows to inject the service with that string name into the matched parameter within the function. So that parameter name can be changed to anything by the minifier and all is well as the only thing that matters is the service's position within the list of strings/parameters (first string matches to first parameter, etc.).
For lots more on dependency injection: http://docs.angularjs.org/guide/di

Where you specify "HERE IS ONE PLACE TO DO IT" is actually where you can inject different modules into another module, here is an example.
var helperModule = angular.module('helperModule', []);
var pageModule = angular.module('pageModule', ['helperModule']);
pageModule now has access to all the services & directives.. etc. linked to helperModule
And where you specify this
function(B: $hereIsAnotherPlace){ ...
Is where you inject services, although that javascript is invalid.
Here are 2 ways that you can inject services.
.controller( 'myController', function( $myService ) { ... });
Or for minified code you would use.
.controller( 'myController', ['$myService', function( $myService ) { ... }]);
in the latter example you can change the name of $myService in the arguments to anything you like.
Quick Example
.controller( 'myController', ['$myService', function( $thisIsEqualTo$myService ) { ... }]);
So the last 2 example's are identical, when you use the Array to specify the injections, the arguments can be named whatever as they are passed in the order that you require them in the Array.

A is only used when creating your module, and should only be used once in your app.
angular.module('myApp', [A: HERE IS ONE PLACE TO DO IT]);
B is for injecting into the controller...
angular.module('myApp')
.controller('HomeController', function(B: $hereIsAnotherPlace){
});
No injection of A again otherwise it will create a new module rather than using your already created one.

Related

Inject a factory in a controller in angular

I´m trying to inject a factory in a controller in Angular, but I can not do. It is my code.
app.controller('ManejadorEventosVista', ['AdministradorMarcador', ManejadorEventosVista]);
'app' is the variable that corresponds to the module with their respective dependencies. The controller is 'ManejadorEventosVista' and requires the services provided by the factory 'AdministradorMarcador'.
function ManejadorEventosVista(){}
but when I want to use the factory 'AdministradorMarcador' in this part of the code , the factory is not recognized.
ManejadorEventosVista.prototype.seleccionarMarcadorOrigen = function (){
AdministradorMarcador.setTipoMarcador(AdministradorMarcador.MARCADOR_ORIGEN);
};
How I can do to use the factory
'AdministradorMarcador' in ManejadorEventosVista.prototype.seleccionarMarcadorOrigen??..
Help or example to guide me??..Thanks..
ManejadorEventosVista needs to take an argument and you will be able to reference the AdministradorMarcador inside the function as whatever you named the first variable. Like so
function ManejadorEventosVista(AdministradorMarcador){/**your code here**/}
What you are doing with the line fragment ['AdministradorMarcador', ManejadorEventosVista] is declaring that your function depends on AdministradorMarcador, but without providing an argument to ManejadorEventosVista, AngularJS doesn't know how you intend to reference AdministradorMarcador inside your controller.
This is done in order to allow AngularJS scripts to be minified, especially by already existing solutions, as they would change the variables your function takes to single letter names, making it impossible for AngularJS to determine which service or factory to inject. Annotation uses strings and position-based ordering to allow your script to work, even after being minified since strings won't be altered by the process.
See also Latest Stable docs on Annotation

How does implicit/inline/$inject dependency injection work in AngularJS?

I'm new to AngularJS and I would like to understand more about the dependencies that are being injected by default. While reading through code I've noticed that sometimes dependencies are explicitly declared beforehand, and sometimes they aren't. For example:
someModule.controller('MyController', ['$scope', 'someService', function($scope, someService) {
// ...
}]);
Gives the same results as:
someModule.controller('MyController', function($scope, someService) {
// ...
});
How does this work? Is Angular assuming that the modules being injected are named the same as the variables in the parameters?
Also, strangely enough, if you do specify the dependencies that are going to be injected, you must specify all of them and in the right order, otherwise nothing will work. For example, this is broken code:
someModule.controller('MyController', ['someService', '$scope', function($scope, someService) {
// Won't give us any errors, but also won't load the dependencies properly
}]);
Can someone clarify to me how is this whole process working? Thank you very much!!
Yes, dependency injection in Angular works via the names of the components you (and Angular - for the internal ones) registered.
Below is an example showing how a service is registered and injected into a controller using several different annotations. Please note that dependency injection always works the same in Angular, i.e. it doesn't matter if you are injecting something into a controller, a directive or a service.
app.service('myService', function () {
// registering a component - in this case a service
// the name is 'myService' and is used to inject this
// service into other components
});
Two use (inject) this component in other components, there are three different annotations I am aware of:
1. Implicit Annotation
You can either specify a constructor function which takes as parameters all the dependencies. And yes, the names need to be the same as when these components were registered:
app.controller('MyController', function ($http, myService) {
// ..
});
2. Inline Array Annotation
Or you can use a notation using an array, where the last parameter is the constructor function with all the injectables (variable names do not matter in this case). The other values in the array need to be strings that match the names of the injectables. Angular can this way detect the order of the injectables and do so appropriately.
app.controller('MyController', ['$http', 'myService', function ($h, m) {
/* Now here you can use all properties of $http by name of $h & myService by m */
// Example
$h.x="Putting some value"; // $h will be $http for angular app
}]);
3. $inject Property Annotation
A third option is to specify the $inject-property on the constructor function:
function MyController($http, myService) {
// ..
}
MyController.$inject = ['$http', 'myService'];
app.controller('MyController', MyController);
The reason why the last two options are available, at least as far as I know, is due to issues which occured when minifying the JavaScript files which led to the names of the parameters being renamed. Angular then wasn't able to detect what to inject anymore. In the second two cases the injectables are defined as strings, which are not touched during minification.
I would recommend to use version 2 or 3, as version 1 won't work with minification/obfuscation. I prefer version 3 as from my point of view it is the most explicit.
You can find some more detailed information in the internet, e.g. on the Angular Developer Guide.
Just to provide a different sort of answer, as to the how inline/implicit dependencies work in AngularJS. Angular does a toString on the provided function and parses the parameter names from the string which is produced. Example:
function foo(bar) {}
foo.toString() === "function foo(bar) {}"
References:
source code
AngularJS Dependency Injection - Demystified

Should I use the array notation in Angular?

I learnt to write angular dependencies needed using the array notation, that way:
var app = angular.module('MyApp', []);
app.controller('MyCtrl', ['$scope', function($scope) {
$scope.stuff = 'stuff';
}]);
The Angular doc follows this notation, but I see more and more tutorials not using the array notation and just directly passing the controller the function($scope).
Is there any differences between the two ways to do? Or maybe one was implemented in the version two?
You should be using the Array notation
say Tomorrow if you wish to minify your data using a uglify say, it minifies your big variable names but doesn't touch your strings so your statement
from
Case 1 with array notation
original
app.controller('MyCtrl', ['$scope', function($scope) {
minified
x.controller('MyCtrl', ['$scope', function(a) {
Here controller knows exactly knows that variable a is $scope
Case 2 without array notation (whereas if you choose not to use it)
original
app.controller('MyCtrl', function($scope) {
minified
x.controller('MyCtrl', function(a){
Now your controller doesn't know what to do with a variable its not $scope for sure
The array notation is important if you plan to minify your code, which you should be doing in production anyway. Stick to using it.
If you're planning to mini your app, yes you MUST use the array notation.
This is because the variables are renamed, so the injector no longer knows what dependencies you're intending to inject.
For example if $scope was renamed a on minification it wouldn't work.
Obviously this means you have to write and maintain more code. Luckily, you can automate this in your build process.
On my project I use grunt and angular-templates.
https://www.npmjs.com/package/grunt-angular-templates
Yes there is a difference. I would recommend continuing to use array notation since not using it will break your dependency references if the application is minified. For more details, read https://docs.angularjs.org/tutorial/step_05 .
Basically you can rename the variables without affecting angulars ability to inject. As others have said, specifically for minification.
var app = angular.module('MyApp', []);
app.controller('MyCtrl', ['$scope', function(s) {
s.stuff = 'stuff';
}]);
Array notation is used for injecting the dependencies. Difference between this two is if u do not include array notation means u do not need any dependencies.
Best practice is to use [] notation so that u can include dependencies any time in you application.
In a nutshell there are two ways how you can use dependency injection in angular: implicit or explicit.
Implicit DI is .controller('MyCtrl', function(scope, dep1, dep2){
It does not give instructions to $injector on what to include, it bvasically doing something like a reflection to figure it out. If you minify your code it will turn into
`.controller('MyCtrl', function(a, b, c){ `
That is why we are using explicit DI. There are several ways to do so:
use array : .controller('MyCtrl', ['$scope','dep1','dep2', function($scope, dep1, dep2){
use $inject:
.controller('MyCtrl', myCtrl)
myCtrl.$inject = ['$scope', 'dep1', 'dep2']
function myCtrl($scope, dep1, dep2) {}
In both ways, even if minification renamed function parameters to something, $injector will know what it is expected to inject, as it has original names in the string literals ( which are not affected by minification)
You can also use comment annotations that will tell compiler/transpiler how to handle angular DI , ie it will turn implicit DI into explicit for you, see ng-annotate
I personally prefer second way with .$inject
DI helps you while minifying the code, Also other answer does explained how both the DI injection techniques work. Basically none of DI injection technique is bad.
But I'll prefer to do not worry about this thing because other will take care of this DI thing. Use ng-annotate directive, that will give you facility to use dependency inside a function directly
If you wrote code like this
angular.module("MyMod").controller("MyCtrl", function($scope, $timeout) {
//awesome code here
});
When you send this code to minifier it does add the array annotation of dependency on the angular component while
angular.module("MyMod").controller("MyCtrl", ["$scope", "$timeout",
function($scope, $timeout) {
//code here
}
]);
From Docs
ng-annotate works by using static analysis to identify common code
patterns. There are patterns it does not and never will understand and
for those you can use an explicit ngInject annotation instead, see
section further down.

AngularJS - Parameter binding in controllers

I've got the following code sample and apparently I once again lack some background information.
When I use the greet directive, there is a binding happening on the controller level. For some reason it binds the element and the attribute values of the element to those corresponding variables ($attr, $element).
Is there a list with all the existing bindings which exist for the controllers?
I've done some research, but couldn't come up with anything in this direction.
directives.js
angular.module('TodoApp.directives', []).
directive('greet', function () {
return {
template: '<h2>Greetings from {{from}} to {{to}}<h2>',
controller: function($scope, $element, $attrs) {
$scope.from = $attrs.from;
$scope.to = $attrs.greet;
}
};
});
list.html
....
<div greet="Test1" from="Test2"></div>
...
Directive controllers have some special bindings. You can find them in the docs of the $compile service (here), search for "controller". Repeating here for completeness:
The controller is injectable (and supports bracket notation) with the following locals:
$scope - Current scope associated with the element
$element - Current element
$attrs - Current attributes object for the element
$transclude - A transclude linking function pre-bound to the correct transclusion scope. The scope can be overridden by an optional first argument. function([scope], cloneLinkingFn).
This is called Dependency Injection. Rather than having a fixed parameter list, Angular uses a DI container to inject the parameter when you need it. For example, if you need the $http service, just add it as a parameter - the order of the parameters does not matter.
The type of parameters you can inject are constants, values, factories, services, and providers.
Some are available to you 'out-of-the-box' from the Angular library:
Look here for Angular Providers and Services
Others, you get from third-party modules, or modules that you develop yourself.

How to dynamically inject controller in AngularJS

How to set ng-controller as an expression from the $scope?
According to the documentation:
ngController – {expression} – Name of a globally accessible
constructor function or an expression that on the current scope
evaluates to a constructor function.
But how to evaluate scope expression as a controller for controllers that have been registered with module .controller?
For example:
Layout:
<div ng-controller="myExpr"></div>
JavaScript (define controller):
app.controller('myCtrl', ['$scope', '$timeout', function () { ... }];
JavaScript (parent scope):
$scope.myExpr = ...;
What should be in myExpr to use myCtrl as a controller via expression?
I've tried $controller('myCtrl')... not working...
P.S. If controller has been defined via globally accessible function.. it's possible to provide it as myExpr. But what to do if it has been defined so?
The expressions that ng-controller accept are a bit wierd. So you can do this by writing your controller slightly differently (but read below as for why you probably don't want to).
function myCtrl($scope) {
$scope.value = 'Stuff';
}
This is a controller and will work like normal for this case. Like in this example: http://jsbin.com/ubevel/2/edit
So why not do it?
First of all this is not a good way to define things from a testing perspective. Secondly, this allows you to set the controller dynamically on load but it won't allow you to change things after that. If you change the value of myExpr after the page has loaded the controller will not change.
So what to do?
I would highly suggest looking at using a service instead. Swap out your actions by supplying your outer controller with a service that you then change in the same manner you are now trying to change the inner controller. So something like: http://jsbin.com/ubevel/5/edit
This service can be swapped out on the fly, changing it will change the actions that are available in the scope.
You could also use an include, but this would result in duplicate html. I personalty am fine with this since I am against reusing html for two different types objects (sooner or later you want to change one but not the other and it becomes a mess). But a lot of people would object to that.
An extra note: There are probably nicer ways to do it with controllers, I probably haven't looked at all angles, but I just don't think controllers are the right tool for this case.

Categories

Resources