What can the controller parameters be?
The controller parameters are dependencies, which are injected by AngularJS injector service. They can be anything. But they are usually the services that will be used inside the controller.
Where are the controller functions called with their parameters?
Controllers, as well as directives, filters, services and so many other things in AngularJS are functions. But the framework manages a lot of when and how these functions are called.
What you call stuff associated has a name: dependency, as mentioned above. What you call magic is AngularJS dependency injection mechanism in action.
When these functions (controllers and others) are called by the injector, it reads the parameters names (for example: $scope
or $http
or angularFire
) and searches for a registered service with that name, which is then provided as the parameter when the function is called.
It is simple. You have several ways to instruct about your "dependencies" (parameters managed by the injector) to the injector.
When you simply declare your function as function myCtrl($scope) {}
, the injector will be able to find the $scope
service from the parameter name. But if you minify the JavaScript code, the injector will not be able to find the service anymore, because the parameter name will be modified to a smaller string, like "a" or "x". To avoid this problem, it is possible to specify the service name to be injected using the array notation. In this case, you would declare your function like this: myCtrl = ['$scope', function($scope) {}]
You will see a lot of array notation usage in AngularJS world. Now you start to understand it. You could even inject $scope
and angularFire
and use them with other names in your function (changing the name is not recommended - this example here comes for learning purposes): ['$scope', 'angularFire', function(skop, af) {}]
- this way, inside the function you can use $scope as "skop" and angularFire as "af". The order of the services in the array matches the order of the parameters.
Another example:
var myController = ['$scope', '$resource', '$timeout',
function($scope, $resource, $timeout) {
// this controller uses $scope, $resource and $timeout
// the parameters are the dependencies to be injected
// by AngularJS dependency injection mechanism
}
];
Where is angularFire defined?
In the firebase module.
As you already know now, the injector will inject anything as long as it has that "thing" name registered and available on its records. If there is a "service" with that name, he is able to provide it.
How, then, is built this name => stuff
list which the injector uses?
Module is the answer. A module is little more than a list of name => stuff
. It is in a module where you register services, factories, filters, directives, and more.
Look carefully at the Module methods at the official documentation... almost all of them receive as parameters: name and some "stuff" (where "stuff" is almost always a function, defining either a controller, or a factory, or a directive). It is all this "stuff" that will become injectable through their specified name.
AngularJS services like "$timeout", "$http" and others are available by default because the ng module is already loaded by the framework.
For additional services, you need to load/require the module. That's what you do with ngRouter, firebase, etc... By loading the module, its registered stuff are available for injection in your module/app.
Let's see a step-by-step example:
// An empty module:
var module = angular.module('myModule', []);
// Now, adding an "injectable" constant:
module.constant('niceStuff', { blip: 'blop', blup: 307 });
// We could add a service:
module.service('entityManager', ['$http', function($http){ }]);
// and so on... if I wanted to use "$resource" instead of "$http"
// in the entityManager service above...
// ...I would need to require the ngResource when creating the module above,
// like this: var module = angular.module('myModule', ['ngResource']);
// because "$resource" is not available by default
// NOW, anywhere else, since the code above already ran
// I can use those NAMES as dependencies like this:
// We are creating another module now:
var koolModule = angular.module('km', ['myModule']);
// Note that I am requiring the previous module through its registered name
// Now, anything I've declared in that module
// - just like "ng" (by default) and "firebase" (required) does -
// is available for "injection"!!!
koolModule.controller('koolController',
['niceStuff', 'entityManager', function(niceStuff, entityManager) {
console.log(niceStuff.blip); // 'blop'
console.log(niceStuff.blup + 10); // 317
}]
);
This is how firebase stuff, like angularFire, becomes available! What have we done? First, we created the "myModule", and registered NAMED stuff to it. Later, we required the "myModule" for our "koolModule" - and those NAMES were already available in the injector name => stuff
list.
How is the fbURL in the parameter linked
As we've just seen, most module methods are merely registering things - giving names to things so they can be injected and/or used through these names later.
When module.value('fbURL', 'https://angularjs-projects.firebaseio.com/')
is called, fbURL (and the specified value) is registered into the name => stuff
list... in this case, the name is "fbURL", and the value/stuff is the URL string - but it could be anything!
Is there a place where I can see all the services, e.g. $location and $timeout, that Angular.js provides?
Yes, the API reference: http://docs.angularjs.org/api/
Pay attention at how the left-side navigation is organized... by modules! First, the ng module, with tons of directives, services, filters, etc. Then, below, the other modules (ngRoute, ngResource, ngMock, and so on), each one containing their own services, fitlers or directives...
Thanks for the opportunity of sharing these thoughts. I enjoyed writing them.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…