AngularJS supports two slightly different syntaxes for dependency injection
Syntax 1
myModule.controller('myCtrl', function($scope, $http, myService) {
...
...
});
Syntax 2
myModule.controller('myCtrl', ['$scope', '$http', 'myService', function($scope, $http, myService) {
...
...
}]);
Is there a fundamental difference between the two syntaxes?
When to use either of the two syntaxes?
Syntax 2 is called type hinting, if you plan to uglify and mangle your code, angular would still know what services to inject.
After mangling and ugilying:
myModule.controller('myCtrl', ['$scope', '$http', 'myService', function(a, b, c) {
Angular would read the string values of the array provided in order to determine the name of the services a, b, c in order to inject them properly.
Related
I see the following angularjs controller syntax structure all the time.
angular.module('7minWorkout').controller('WorkoutController',
['$scope', '$interval', '$location',
function ($scope, $interval, $location)
{
}]);
Why the repetition in the parameter names? Why not just like this
angular.module('7minWorkout').controller('WorkoutController',
['$scope', '$interval', '$location',
function ()
{
}]);
or
angular.module('7minWorkout').controller('WorkoutController',
[
function ($scope, $interval, $location)
{
}]);
The array syntax will help you with minification/uglify of your js code.
angular.module('7minWorkout').controller('WorkoutController',
function ($scope, $interval, $location) {
// code here
});
Will be minified and mangled as:
angular.module('7minWorkout').controller('WorkoutController',
function (a, b, c) {
// code here
});
So Angular won't be able to figure out which dependencies to inject
On the other hand, using the array declaration:
angular.module('7minWorkout').controller('WorkoutController',
['$scope', '$interval', '$location', function ($scope, $interval, $location) {
// code here
}]);
Will be minified as :
angular.module('7minWorkout').controller('WorkoutController',
['$scope', '$interval', '$location', function (a, b, c) {
// code here
}]);
So Angular will know what a, b and c represent.
There's also another way to inject variables if you use your first example code like below:
WorkoutController.$inject = ['$scope', '$interval', '$location'];
or
angular.module('7minWorkout').controller('WorkoutController', /* #ngInject */
function ($scope, $interval, $location) {
// code here
});
which will create the $inject method mentioned above when the code is annotated.
So, there are mainly four kinds of annotation:
Implicit Annotation - the first example code
Inline Array Annotation - the second example code
$inject Property Annotation - the $inject method
$ngInject Comment Annotation - the #ngInject method
ng-annotate
Tools like ng-annotate let you use implicit dependency annotations in your app and automatically add inline array annotations prior to minifying. If you decide to take this approach, you probably want to use ng-strict-di.
For more information, see AngularJS Developer Guide - Using Strict Dependency Injection.
This "repetion" is to make it safe for minification:
AngularJS - Controllers, Dependencies, and Minification
or you can use following syntax, according to popular angular-styleguide https://github.com/johnpapa/angular-styleguide
angular.module('7minWorkout')
.controller('WorkoutController', WorkoutController);
WorkoutController.$inject = ['$scope', '$interval', '$location'];
function WorkoutController($scope, $interval, $location) {
}
You could write the first version since it just omits the parameters of the function which are also accesible via arguments inside the function. So you would avoid the repition but slicing the arguments property is also not really efficient (compared to just type out the parameters).
As the others answers stated the repition is to make it safe for minification.
The first controller syntax makes it possible to minify/uglify the javascript code with the use of tools like ngmin. I'm not quite sure if the 2nd and 3rd options you have provided are viable options to create a controller, but in any case they will not be minified correctly since the tools will not now what the providers are. I would either suggest to use option 1 or option 3 (without the brackets) to create a controller. Note that option 3 will not be minified correctly by automated tools.
Some Useful information about creating controllers:
AngularJS Developer Guide - Controllers
option3 without brackets
angular.module('7minWorkout').controller('WorkoutController', function ($scope, $interval, $location)
{
//Your Code
});
I'm getting an error when running my app: Argument 'AppCtrl' is not a function, got undefined - I believe it has something to do with the separation of controller files?
Ok, so I have in my first file: app.js this:
angular.module('zerochili', [
'ionic',
'zerochili.controllers',
'zerochili.services',
'zerochili.directives'
])
Then I have some different controller files - Lets take the file there the AppCtrl is - This looks like the following:
angular.module('zerochili.controllers', [])
.controller('AppCtrl', ['$scope', '$ionicModal', '$timeout', function ($scope, $ionicModal, $timeout){
}])
And another file fx like so:
angular.module('zerochili.controllers', [])
.controller('LoginCtrl', ['$scope', '$state', '$ionicPopup', '$timeout', function($scope, $state, $ionicPopup, $timeout){
}]);
What am I doing wrong? Can't seem to quite figure it out?
You can think of your app.js as the part where you define the module. The controller files should be adding to the module (not have ", []" as this creates the module).
You should probably be doing this in your controllers
angular.module('zerochili')
.controller('AppCtrl', etc.
So, remove these: 'zerochili.controllers', 'zerochili.services', 'zerochili.directives' from the app.js and add your controllers,services and directives to the 'zerochili' module like above.
If you want to keep, for example, 'zerochili.controllers' make sure that you don't create this module twice, i.e. have angular.module('zerochili.controllers', []) twice.
It's only because you are creating a new modul with []
you can put at the end of your app.js
angular.module('zerochili.controllers', []);
angular.module('zerochili.directives', []);
angular.module('zerochili.services', []);
and then use it like this:
angular.module('zerochili.controllers')...
I would like to understand the difference between the declaration of MyOtherService and MyOtherComplexService. Especially what is the purpose of square bracket part? When to use them and when not?
var myapp = angular.module('myapp', []);
myapp.factory('MyService', function($rootScope, $timeout) {
return {
foo: function() {
return "MyService";
}
}
});
myapp.factory('MyOtherService', function($rootScope, $timeout, MyService) {
return {
foo: function() {
return "MyOtherService";
}
}
});
myapp.factory('MyOtherComplexService', ['$rootScope', '$timeout', 'MyService', function($rootScope, $timeout, MyService) {
return {
foo: function() {
return "MyOtherComplexService";
}
}
}]);
myapp.controller('MyController', function($scope, MyOtherService, MyOtherComplexService) {
$scope.x = MyOtherService.foo();
$scope.y = MyOtherComplexService.foo();
});
It enables AngularJS code to be minified. AngularJS uses parameter names to inject the values to your controller function. In JavaScript minification process, these parameters are renamed to shorter strings. By telling which parameters are injected to the function with a string array, AngularJS can still inject the right values when the parameters are renamed.
To add to Ufuk's answer:
ngmin - compiles your standard modules to min-safe modules
Angular's min-safe square bracket notation is cleary less convenient, because you have to type every dependency twice and argument order matters. There is a tool called ngmin which compiles your standard modules to min-safe modules, so you don't have to manage all those things by hand.
Angular + CoffeeScript
If you're using CoffeeScript the situation is even worse. You may choose between ngmin, which will destroy your source map, or if you want to write it out all by yourself you'll have to wrap your entire code with square brackets, which is super ugly.
angular.module('whatever').controller 'MyCtrl', ['$scope', '$http' , ($scope, $http) ->
# wrapped code
]
In my opinion this is not a CoffeeScript flaw, but a poor design decision of the Angular team, because it's against all JS/CoffeeScript conventions not to have the function as the last argument. Enough ranting, here is a little helper function to work around it:
deps = (deps, fn) ->
deps.push fn
deps
This is a very simple function that accepts two arguments. The first one is an array of strings containing your dependencies, the second one is your module's function. You may use it like this:
angular.module('whatever').controller 'MyCtrl', deps ['$scope', '$http'] , ($scope, $http) ->
# unwrapped code \o/
Just to exemplify what was already said, if you use the following syntax:
myapp.factory('MyService', function($scope, $http, MyService) { ... });
most of the JS minifiers will change it to:
myapp.factory('MyService', function(a, b, c) { ... });
since functions argument names usually can be renamed for shorter names. This will break the Angular code.
In Angular, to get your code minifiable in all minifiers, you use the bracket syntax:
myapp.factory('MyService', ['$scope', '$http', 'MyService', function($scope, $http, MyService) { ... }]);
that will be minified to:
myapp.factory('MyService', ['$scope', '$http', 'MyService', function(a, b, c) { ... }]);
Note that minifiers do not touch on strings so Angular will see the minified code and match arguments in order:
$scope = a
$http = b
MyService = c
To avoid this ugly square bracket syntax, you should use smart minifiers like ng-annotate.
As of now, ng-min is deprecated.
Use ng-annotate instead.
It is good practice to use ng-annotate in your build job so you don't have to deal with the min-safe / bracket notation when developing, as it makes the code harder to read and maintain.
There is a grunt-plugin and a gulp plugin available on npm.
I have been struggling to find a solution for this problem:
I am using Angular.js on my site.
I use CoffeeScript to write my JavaScript in Visual Studios 2012. When I save my CoffeeScript - both a minified and un-minified version of the JavaScript file is saved.
While using any minified version of JavaScript files on my webpage I keep getting this error:
Error: $injector:unpr
Unknown Provider
Unknown provider: nProvider <- n
This error results from the $injector being unable to resolve a required dependency. To fix this, make sure the dependency is defined and spelled correctly. For example:
If I don’t use the minified version everything works fine.
Here is how I use angular to declare my App in each JavaScript file
(function() {
var myApp;
myApp = angular.module("myApp", ['uploaddirective']);
myApp.controller("videoController", function($scope, $http, $compile) {
Can anyone help me to resolve this issue?
Use array notation for minification-safe dependency injection:
myApp.controller("videoController", ['$scope', '$http', '$compile', function($scope, $http, $compile) {
Another option is to use build task to automatically annotate injections for you:
// #ngInject
myApp.controller("videoController", function($scope, $http, $compile) {
You will need grunt or gulp plugin for this, like ngAnnotate.
https://docs.angularjs.org/guide/di
When you minifying the above code you will get something like
a.controller("videoController", function(x, y, z) {...
note that $scope, $http, $compile are replaced with x, y, z.
So when angular starts to execute Controller can't find the injectors because $scope, $http, $compile are replaced with x, y, z and there are no inject-able providers as x, y or z, so angular will trigger a error saying Unknown Provider.
solution 1
you can follow Inline Array Annotation as,
myApp.controller("videoController", ['$scope', '$http', '$compile', function($scope, $http, $compile) {...
after minifying this you will get something like
a.controller("videoController", ['$scope', '$http', '$compile', function(x, y, z) {...
minifying process doesn't effect on strings, here angular will use a array with strings that will match the providers. Here angular will match the x with $scope and so on.
solution 2
$inject Property Annotation
var MyController = function($scope, $http, $compile) {
// controller function ...
}
MyController.$inject = ['$scope', '$http', '$compile'];
myApp.controller("videoController", MyController);
additionally check ng-annotate
While this thread sums up the following three code styles:
1)
angular.module('mainCtrl', []);
function MainCrl($scope, $rootScope) {}
2)
angular.module('mainCtrl',[])
.controller('MainCtrl', function($scope, $rootScope)) { ... });
3)
angular.module('mainCtrl',[])
.controller('MainCtrl', ['$scope', '$rootScope', function(scope, rootScope)) { ... }]);
there's a fourth way i've seen in this video that is very appealing for me
4)
var controllers = {}
controllers.mainCtrl = function($scope, $rootScope){ };
app.controller(controllers)
I am leaning towards continuing with 4), will it break if minified or are there any other drawbacks? Should i just go with 3) since it seems to be the standard way of doing it?
Option 1 pollutes global namespace and hinders minification and does not respect modules.
Option 2 does not let you rename your injectables in controller signature.
Option 4 pollutes global namespace, but it is minification-safe if you do it properly1.
Option 3 lets you rename your injectables2, does respect modules, does not pollute global namespace, and does not require any extra work when minifying.
So my winner is option #3.
1 Option 4 - minification-friendly version:
var controllers = {};
controllers.mainCtrl = ['$scope', '$rootScope', function($scope, $rootScope){ ... }];
app.controller(controllers);
2 Renaming injectables:
app.controller('MyCtrl', ['$scope', 'UserService', function($scope, User){ ... }]);
My recommendation: Go with option 3, for three reasons:
It is (IMHO) the most widely adopted one.
You have no problems with minification (and this is the only option where this is true).
It works best with modules.