Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Confused about AngularJS dependency injection inconsistency

I am new to angular.js, and went through several tutorials, including all of the ones here on codeschool. I found them very useful, and learned a lot. But now that I have finished my "introduction" and am getting into trying to use it in some things, I am finding some confusing inconsistencies, most notably, "dependency injection".

In the tutorials I took, dependencies for services were done like this;

app.controller('name', [ $http, $scope, function($http, $scope) {
   // .. code ... //
}]);

This strikes me as odd, but it works anyway. I was confused as to why the [] didn't terminate before the function (I am presuming this is what you refer to as a 'callback' function in javascript?). I was expecting it more like require.js where it would have been ...

app.controller('name', [ '$http', '$scope' ], function($http, $scope) { });

However then I began to look at examples and demos of angular online, and I found this wasn't consistent. For instance, examine the following links;

  • medium.com
  • revolunet.com
  • kendo.ui

In each of these, I see dependency injection used like this;

app.controller('name', function($http, AdvancedGithubUser) { });
app.controller('name', function($scope){ });
function controllerName($scope) { };

They completely bypass the array like syntax, and all three are different. In one, it takes a type of object that is declared somewhere else, but I don't see any wiring done to point to it.

In another, it just kind of has these objects.

And still in another, the 'name' part of the controller is the name of the function, and I see nothing that really denotes it as a controller, but it is used that way in directives.

Can anyone explain this to me? I am completely lost now. This inconsistency makes it a bit difficult to pick up the techniques.

like image 485
Ciel Avatar asked Jul 29 '14 16:07

Ciel


2 Answers

There are THREE ways to annotate dependencies on a function:

The first one is: be explicit and exact with the parameter names:

app.controller('ACtrl', function($scope, $http, $q) {
    //your stuff here
});

This one implies that the function has param names which must match the names of services/providers already registered. Caveat: if you minify the file (uglify, to save space), you will lose the parameter names, and so it will be broken - will not work (will complain).

The second one lets you choose the service names to inject as string literals (since a string literal is a value, it is never minified):

var myfunc = function($s, $http, $q) {
    //do your stuff here
};
myfunc.$inject = ['$scope', '$http', '$q'];
app.controller('ACtrl', myfunc);

This makes Angular read the $inject property of the function, and match formal parameters not by name, but by value in the same array position. So $scope will be in $s even if you minify the file. If $inject does not exist in the function, then you're back in the first - and not recommended - case.

The third one is similar to the second (i.e. it will specify dependencies by strings and will resist the uglyfication):

var myfunc = function($s, $http, $q) {
    //do your stuff here
}
app.controller('ACtrl', ['$scope', '$http', '$q', myfunc]);

Notice that the last element of the array is the function to call. It looks a bit creepy, but it is consistent. Angular does this: If the controller is an array, the last element is popped - it will be the function. The former elements (the remaining array) are threated exactly as if they were the $inject value in the function.

Controllers and providers must have a name to reference them - I used 'ACtrl' as name of the controller. It's not the name of the function, but an internal name to use in Dependency Injection (for providers) and stuff like ngRoute (for controllers).

Declaring the name is the first bound of the wiring you are asking for. Using them in any of the three forms of Dependency Injection is the second bond of such wiring.

Remember: AdvancedGithubUser is a registered provider, as is $http. The only difference is that $http is built-in in Angular, and AdvancedGithubUser is not. Dollar-sign-starting symbols should be reserved to Angular, but it is not a requirement - just a good practice. AdvancedGithubUser was created (in an external module) with something like:

app.service('AdvancedGithubUser', AdvancedGithubUser);
//being AdvancedGithubUser a constructor.
like image 58
Luis Masuelli Avatar answered Oct 05 '22 23:10

Luis Masuelli


Both

myApp.controller('ACtrl', ['$scope', function($scope) { }])

and

myApp.controll('ACtrl', function($scope) { });

are allowed. However, according to the docs, the first method (array notation) is recommended.

"This avoids the creation of global functions for controllers and also protects against minification".

like image 26
user2882597 Avatar answered Oct 06 '22 01:10

user2882597