18

Let's say I have made a module with a service and a controller in Angular.js, I am able to access that service inside of the controller like so:

var myapp = angular.module('my-app', []);

myapp.factory('Service', function() {
  var Service = {};
  Service.example = 'hello';
  //etc..
  return Service;
});

myapp.controller('mainController', function($scope, Service) {
  $scope.greeting= Service.example;
});

In this example, the Service object will be passed to the controller, and structuring the code like so will not change the behavior of the code:

myapp.controller('mainController', function(Service, $scope) {
  $scope.greeting= Service.example;
});

so, how does Angular.js "know" what the function arguments mean?

4
  • 1
    Are you quite sure you can reverse the arguments like that? It would be very surprising. Commented Jun 5, 2013 at 21:08
  • 3
    Wow is the Angular documentation beautiful....and utterly, completely impossible to navigate if you don't already know Angular well. Heck, I can't even find that function in the API docs. I can find something that looks vaguely like it here, but the example is a "note" and is passing something completely different as the second argument. Commented Jun 5, 2013 at 21:17
  • @T.J.Crowder - Yes... it is a bit of a mess. I've been pretty much living in there for the last couple of weeks and still find that it's easier to browser the source on GitHub. Commented Jun 5, 2013 at 21:18
  • 2
    @T.J.Crowder yes, it does in fact work that way, and it is very surprising. That's what led me to post this question, I was very confused and curious when I saw Angular worked this way. Commented Jun 6, 2013 at 13:03

2 Answers 2

21

Angular simply parses the toString() representation of the function for the names of dependencies. From the docs:

In JavaScript calling toString() on a function returns the function definition. The definition can then be parsed and the function arguments can be extracted.

However, note that this approach will fail if your code is minified. For that reason, Angular supports an alternative (I would suggest always using it) syntax, using an array:

myapp.controller('mainController', ["$scope", "Service", function($scope, Service) {
  $scope.greeting= Service.example;
}]);
Sign up to request clarification or add additional context in comments.

8 Comments

Of course, that's non-standard. Function#toString isn't defined anywhere as returning the source. It works on every desktop browser I've ever seen, but...
@T.J.Crowder - Indeed, but Angular does have a comprehensive test suite, and Function.prototype.toString works as expected in all of its supported browsers. But I only ever use the safer array syntax.
Can you explain how what you linked to relates to the question? Don't get me wrong: I know your contributions here well enough to know (on faith) that they're related. But a comment explaining the link would help people floundering in the beautiful but awful docs... :-)
@T.J.Crowder - The docs page I linked to is part of the AUTO module, which as far as I understand is effectively a base module providing functionality used throughout the framework. It provides the injection functionality that is the subject of this question, although I agree it is completely unclear how all of this links together from the docs. I just knew what I was looking for (anything to do with dependency injection can be found in that page) - I'm very much still getting to grips with Angular.
toString on a function, eh? Gross. But it explains the nagging named-argument magic. As someone brand new to Angular, this seems like an over-engineered "solution" that breaks a cardinal rule of variable naming -- that its name shouldn't matter. I'll be sticking with the require-esque explicitly defined dependency arrays.
|
6

This is accomplished by the quite clever method annotate (source) which takes a regex scan on function signature source (using function.toString()) and iteratively pushes each function argument into the function $inject array.

The same result is accomplished when manually specifying the $inject array as in:

var MyController = function($scope, myService) {
  // ...
}
// Define function dependencies
MyController.$inject = ['$scope', 'myCustomService'];

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.