6

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;

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.

2
  • 1
    Angular uses the argument names to determine what dependencies to inject. The array syntax is there because JavaScript minifiers in many cases will change argument names, so the injector will use the strings in the array if supplied (the strings will survive minification). I believe the difference in syntax between how Angular and Require declare their dependencies is simply because they chose to implement it differently. Commented Jul 29, 2014 at 16:40
  • That makes a lot of sense, actually. Commented Jul 29, 2014 at 17:18

3 Answers 3

7

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.
Sign up to request clarification or add additional context in comments.

2 Comments

This makes sense. Sorry it took me so long to read over, I'm very new to angular, and I wanted to try everything you posted to see if I truly grasped it. Thank you so much for all of your time in typing this up for me. I really appreciate it.
Hm, I still don't like that the callback isn't a third parameter, but I guess that's my problem.
4

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".

4 Comments

I guess my biggest point of confusion comes with the advent of non-built in objects. For example, the AdvancedGithubUser example - I'm having a difficult time seeing how that could work if the object were created in a different file, using these inconsistent methods.
What's hard about it? myApp.controller('ACtrl',['$scope','AdvancedGithubUser',function($scope,AdvancedGithubUser) { }]);
I recommend reading AngularJS dependency injection documentation. It explains how Angular handles DI.
I am not sure what is hard about it. It's just ... not clicking. I'll keep reading, though. Sorry, I am slow to pick up things at times.
1

You can use either direct dependency injection, as in your links, or named dependency injection (with the array).

I would recommend the named syntax as a minimifier such as uglify will compress the variables names. With the array, you name the object to inject and then (last parameter, the function) you use it, so angular will still know what object you want after variables compression.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.