0

I'm going through this Angular tutorial and have noticed that variables seem to be freely added to functions as needed. I figured up to this point that there were just conventions but am I mistaken? Consider the last file:

app.controller('PostsCtrl', function ($scope, $location, Post) {
    $scope.posts = Post.all;

    $scope.post = {url: 'http://'};

    $scope.submitPost = function () {
      Post.create($scope.post).then(function (ref) {
        $location.path('/posts/' + ref.name());
      });
    };

Here "$location" was added to function() between $scope and Post. Is $location the only option for the 2-nd parameter in an anonymous function here with 3 parameters or is angular somehow looking at the name of the 2nd parameter and deducing that it needs to inject $location there? Where in the documentation can I see all the conventions for 1, 2, 3, etc parameter versions of this function?

This code doesn't appear to work btw. Post is undefined.

1
  • 1
    it is an anonymous function. there is no "standard parameter list" for an anonymous function, the controller function's second argument is the anonymous function, and within the controller function, it can send 3 arguments to the anonymous function. Commented May 21, 2014 at 22:14

5 Answers 5

4

With angular, the names are significant; in plain javascript, not really.

With that, however, if you wanted them to be insignificant in the example above, you could do:

app.controller('PostsCtrl', ['$scope','$location', 'Post', 
    function (foo, bar, foobar) {
        ....
    }
]);

In which case you're mapping the first, second, and third parameters to $scope, $location, and Post respectively. This is actually a better way to do this, as when you minify with angular, it is going to change the names of those parameters, and it is going to break it.

Sign up to request clarification or add additional context in comments.

Comments

2

In this case, you could order those parameters however you'd like. Angular uses Dependency Injection to supply dependencies to your controller. In this case, it will infer those dependencies based on the names so you can order them however you'd like.

Unfortunately, using this method with minification is impossible. In that case, you need to specify your dependencies. Your controller function must then take them in the order that they are defined:

app.controller('PostsCtrl', 
    ['$scope','$location','Post', function($scope, $location, Post) {
}]);

Comments

1

Ahh I was confused by this at first as well. Angular has a nice feature called Dependency Injection. The names do matter because angular looks at the names of the parameters and decides what object to pass to the function. In this rare case in JavaScript, names matter but order does not.

Comments

1

Since you already have other answers with Angular specific instructions, I'll try to explain in simple code how Angular achieves dependency injection in JavaScript and how one might go about doing something similar in plain JS.

First it turns the function into a string, then reads the parameters, and extracts the property from the dependency container, and finally calls the function with the parameters that were extracted. Here's a very simple example of how one might do this:

// parses function and extracts params
function di(app, f) {
  var args = f.toString()
    .match(/function\s+\w+\s*?\((.*?)\)/)[1] // weak regex...
    .split(/\s*,\s*/)
    .map(function(x){return app[x.replace(/^\$/,'')]})
  return function() {
    return f.apply(this, args)
  }
}

// Example

// A dependency container
var app = {
  foo: 'this is foo',
  baz: 'this is baz'
}

// this is like adding a function with the Angular ecosystem
// the order of arguments doesn't matter
var test = di(app, function test($foo, $baz) {
  console.log($foo) //=> this is foo
  console.log($baz) //=> this is baz
})

// No need to pass arguments, they were injected
test()
//$ this is foo
//$ this is baz

But handling DI by stringifying the function has drawbacks; old browsers don't support Function.prototype.toString very well, and minifiers will shorten the variable names making this technique useless. But Angular can get around this problem by injecting strings and parameters that match those strings; minifiers won't touch the strings.

Obviously AngularJS does much more than this, but hopefully it will clear your mind on "how the hell is this possible?!" -- Well, it's kind of a hack.

Comments

1

The answer here is really Angular specific. You want to read about Dependency Injection here:

https://docs.angularjs.org/guide/di

The Implicit Dependencies section should be good reading. In short, the name does matter for Angular but the order does not. This is NOT the case for raw JavaScript however.

From the Angular site:

There are three equivalent ways of annotating your code with service name information:

Implicitly from the function parameter names

Using the $inject property annotation

Using the inline array annotation

1 Comment

No problem! There are a lot of good answers here but if this (or one of the others) answered your question, please check the checkmark next to the answer to mark it answered. Thanks!

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.