2011-07-17 08:05:43 +00:00
|
|
|
'use strict';
|
|
|
|
|
|
Introduced injector and $new to scope, and injection into link methods and controllers
- added angular.injector(scope, services, instanceCache) which returns inject
- inject method can return, instance, or call function which have $inject
property
- initialize services with $creation=[eager|eager-publish] this means that
only some of the services are now globally accessible
- upgraded $become on scope to use injector hence respect the $inject property
for injection
- $become should not be run multiple times and will most likely be removed
in future version
- added $new on scope to create a child scope
- $inject is respected on constructor function
- simplified scopes so that they no longer have separate __proto__ for
parent, api, behavior and instance this should speed up execution since
scope will now create one __proto__ chain per scope (not three).
BACKWARD COMPATIBILITY WARNING:
- services now need to have $inject instead of inject property for proper
injection this breaks backward compatibility
- not all services are now published into root scope
(only: $location, $cookie, $window)
- if you have widget/directive which uses services on scope
(such as this.$xhr), you will now have to inject that service in
(as it is not published on the root scope anymore)
2010-10-09 00:30:13 +00:00
|
|
|
/**
|
2010-11-25 06:50:34 +00:00
|
|
|
* @ngdoc function
|
|
|
|
|
* @name angular.injector
|
|
|
|
|
* @function
|
|
|
|
|
*
|
|
|
|
|
* @description
|
2011-04-18 23:33:30 +00:00
|
|
|
* Creates an injector function that can be used for retrieving services as well as for
|
|
|
|
|
* dependency injection (see {@link guide/dev_guide.di dependency injection}).
|
2011-02-17 00:18:35 +00:00
|
|
|
*
|
2011-10-17 23:56:56 +00:00
|
|
|
* Creating an injector doesn't automatically create all of the `$eager`
|
|
|
|
|
* {@link angular.service services}. You have to call `injector.eager()` to initialize them.
|
2010-11-25 06:50:34 +00:00
|
|
|
*
|
2011-09-01 07:06:09 +00:00
|
|
|
* @param {Object.<string, function()>=} [factories=angular.service] Map of the service factory
|
2011-04-18 23:33:30 +00:00
|
|
|
* functions.
|
|
|
|
|
* @returns {function()} Injector function:
|
2011-02-17 00:18:35 +00:00
|
|
|
*
|
2011-04-18 23:33:30 +00:00
|
|
|
* * `injector(serviceName)`:
|
2011-09-01 07:06:09 +00:00
|
|
|
* * `serviceName` - `{string=}` - Name of the service to retrieve.
|
2011-02-17 00:18:35 +00:00
|
|
|
*
|
2011-04-18 23:33:30 +00:00
|
|
|
* The injector function also has these properties:
|
2010-11-25 06:50:34 +00:00
|
|
|
*
|
2011-09-01 07:06:09 +00:00
|
|
|
* * An `invoke` property which can be used to invoke methods with dependency-injected arguments.
|
2011-11-02 04:09:54 +00:00
|
|
|
* `injector.invoke(self, fn, locals)`
|
2011-09-01 07:06:09 +00:00
|
|
|
* * `self` - The "`this`" to be used when invoking the function.
|
|
|
|
|
* * `fn` - The function to be invoked. The function may have the `$inject` property that
|
|
|
|
|
* lists the set of arguments which should be auto-injected.
|
2011-07-16 08:15:37 +00:00
|
|
|
* (see {@link guide/dev_guide.di dependency injection}).
|
2011-11-02 04:09:54 +00:00
|
|
|
* * `locals(array)` - Optional array of arguments to pass to the function
|
|
|
|
|
* invocation after the injection arguments.
|
2011-09-01 07:06:09 +00:00
|
|
|
* * An `eager` property which is used to initialize the eager services.
|
2011-04-18 23:33:30 +00:00
|
|
|
* `injector.eager()`
|
Introduced injector and $new to scope, and injection into link methods and controllers
- added angular.injector(scope, services, instanceCache) which returns inject
- inject method can return, instance, or call function which have $inject
property
- initialize services with $creation=[eager|eager-publish] this means that
only some of the services are now globally accessible
- upgraded $become on scope to use injector hence respect the $inject property
for injection
- $become should not be run multiple times and will most likely be removed
in future version
- added $new on scope to create a child scope
- $inject is respected on constructor function
- simplified scopes so that they no longer have separate __proto__ for
parent, api, behavior and instance this should speed up execution since
scope will now create one __proto__ chain per scope (not three).
BACKWARD COMPATIBILITY WARNING:
- services now need to have $inject instead of inject property for proper
injection this breaks backward compatibility
- not all services are now published into root scope
(only: $location, $cookie, $window)
- if you have widget/directive which uses services on scope
(such as this.$xhr), you will now have to inject that service in
(as it is not published on the root scope anymore)
2010-10-09 00:30:13 +00:00
|
|
|
*/
|
2011-02-15 06:12:45 +00:00
|
|
|
|
|
|
|
|
function angularServiceInject(name, fn, inject, eager) {
|
|
|
|
|
angularService(name, fn, {$inject:inject, $eager:eager});
|
|
|
|
|
}
|
2011-02-17 00:18:35 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @returns the $inject property of function. If not found the
|
|
|
|
|
* the $inject is computed by looking at the toString of function and
|
2011-04-18 23:33:30 +00:00
|
|
|
* extracting all arguments which and assuming that they are the
|
2011-02-17 00:18:35 +00:00
|
|
|
* injection names.
|
|
|
|
|
*/
|
2011-07-06 13:59:28 +00:00
|
|
|
var FN_ARGS = /^function\s*[^\(]*\(([^\)]*)\)/m;
|
2011-02-17 00:18:35 +00:00
|
|
|
var FN_ARG_SPLIT = /,/;
|
2011-04-18 23:33:30 +00:00
|
|
|
var FN_ARG = /^\s*(.+?)\s*$/;
|
2011-02-17 00:18:35 +00:00
|
|
|
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
|
2011-08-02 20:29:12 +00:00
|
|
|
function inferInjectionArgs(fn) {
|
2011-02-17 00:18:35 +00:00
|
|
|
assertArgFn(fn);
|
|
|
|
|
if (!fn.$inject) {
|
|
|
|
|
var args = fn.$inject = [];
|
|
|
|
|
var fnText = fn.toString().replace(STRIP_COMMENTS, '');
|
|
|
|
|
var argDecl = fnText.match(FN_ARGS);
|
|
|
|
|
forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){
|
2011-04-18 23:33:30 +00:00
|
|
|
arg.replace(FN_ARG, function(all, name){
|
|
|
|
|
args.push(name);
|
2011-02-17 00:18:35 +00:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
return fn.$inject;
|
2011-03-29 06:15:28 +00:00
|
|
|
}
|
2011-10-31 22:33:52 +00:00
|
|
|
|
|
|
|
|
///////////////////////////////////////
|
2011-11-02 04:09:54 +00:00
|
|
|
function createInjector(modulesToLoad, moduleRegistry) {
|
2011-10-31 22:33:52 +00:00
|
|
|
var cache = {},
|
|
|
|
|
$injector = internalInjector(cache),
|
|
|
|
|
providerSuffix = 'Provider',
|
|
|
|
|
providerSuffixLength = providerSuffix.length;
|
|
|
|
|
|
2011-11-02 04:09:54 +00:00
|
|
|
value('$injector', $injector);
|
|
|
|
|
value('$provide', {service: service, factory: factory, value: value});
|
2011-10-31 22:33:52 +00:00
|
|
|
|
2011-11-02 04:09:54 +00:00
|
|
|
function service(name, provider) {
|
2011-10-31 22:33:52 +00:00
|
|
|
if (isFunction(provider)){
|
|
|
|
|
provider = $injector.instantiate(provider);
|
|
|
|
|
}
|
|
|
|
|
if (!provider.$get) {
|
|
|
|
|
throw Error('Providers must define $get factory method.');
|
|
|
|
|
}
|
|
|
|
|
cache['#' + name + providerSuffix] = provider;
|
|
|
|
|
};
|
2011-11-02 04:09:54 +00:00
|
|
|
function factory(name, factoryFn) { service(name, { $get:factoryFn }); };
|
|
|
|
|
function value(name, value) { factory(name, valueFn(value)); };
|
2011-10-31 22:33:52 +00:00
|
|
|
|
|
|
|
|
function internalInjector(cache) {
|
|
|
|
|
var path = [];
|
|
|
|
|
|
|
|
|
|
function injector(value) {
|
|
|
|
|
switch(typeof value) {
|
|
|
|
|
case 'function':
|
|
|
|
|
return invoke(null, value);
|
|
|
|
|
case 'string':
|
2011-11-02 04:09:54 +00:00
|
|
|
var instanceKey = '#' + value,
|
|
|
|
|
instance = cache[instanceKey];
|
|
|
|
|
if (instance !== undefined || cache.hasOwnProperty(instanceKey)) {
|
|
|
|
|
return instance;
|
2011-10-31 22:33:52 +00:00
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
path.unshift(value);
|
|
|
|
|
var providerKey = instanceKey + providerSuffix,
|
|
|
|
|
provider = cache[providerKey];
|
|
|
|
|
if (provider) {
|
|
|
|
|
return cache[instanceKey] = invoke(provider, provider.$get);
|
|
|
|
|
} else {
|
|
|
|
|
throw Error("Unknown provider for '" + path.join("' <- '") + "'.");
|
|
|
|
|
}
|
|
|
|
|
} finally {
|
|
|
|
|
path.shift();
|
|
|
|
|
}
|
|
|
|
|
case 'object':
|
|
|
|
|
if (value instanceof Array) {
|
|
|
|
|
return invoke(null, value);
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
throw Error('Injector expects name or function.');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-02 04:09:54 +00:00
|
|
|
function invoke(self, fn, locals){
|
2011-10-31 22:33:52 +00:00
|
|
|
var args = [],
|
|
|
|
|
$inject,
|
|
|
|
|
|
2011-11-02 04:09:54 +00:00
|
|
|
length,
|
|
|
|
|
key;
|
|
|
|
|
|
|
|
|
|
if (fn instanceof Function) {
|
|
|
|
|
$inject = inferInjectionArgs(fn);
|
|
|
|
|
length = $inject.length;
|
|
|
|
|
} else {
|
|
|
|
|
if (fn instanceof Array) {
|
|
|
|
|
$inject = fn;
|
2011-10-31 22:33:52 +00:00
|
|
|
length = $inject.length;
|
2011-11-02 04:09:54 +00:00
|
|
|
fn = $inject[--length];
|
|
|
|
|
}
|
|
|
|
|
assertArgFn(fn, 'fn');
|
|
|
|
|
}
|
2011-10-31 22:33:52 +00:00
|
|
|
|
|
|
|
|
while(length--) {
|
2011-11-02 04:09:54 +00:00
|
|
|
key = $inject[length];
|
|
|
|
|
args.unshift(
|
|
|
|
|
locals && locals.hasOwnProperty(key)
|
|
|
|
|
? locals[key]
|
|
|
|
|
: injector($inject[length], path)
|
|
|
|
|
);
|
2011-10-31 22:33:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (self ? -1 : args.length) {
|
|
|
|
|
case 0: return fn();
|
|
|
|
|
case 1: return fn(args[0]);
|
|
|
|
|
case 2: return fn(args[0], args[1]);
|
|
|
|
|
case 3: return fn(args[0], args[1], args[2]);
|
|
|
|
|
case 4: return fn(args[0], args[1], args[2], args[3]);
|
|
|
|
|
case 5: return fn(args[0], args[1], args[2], args[3], args[4]);
|
|
|
|
|
case 6: return fn(args[0], args[1], args[2], args[3], args[4], args[5]);
|
|
|
|
|
case 7: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
|
|
|
|
|
case 8: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
|
|
|
|
|
case 9: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]);
|
|
|
|
|
case 10: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]);
|
|
|
|
|
default: return fn.apply(self, args);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
injector.invoke = invoke;
|
|
|
|
|
injector.instantiate = function(Type, locals){
|
|
|
|
|
var Constructor = function(){},
|
|
|
|
|
instance;
|
|
|
|
|
Constructor.prototype = Type.prototype;
|
|
|
|
|
instance = new Constructor();
|
|
|
|
|
return invoke(instance, Type, locals) || instance;
|
|
|
|
|
};
|
|
|
|
|
return injector;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
forEach(modulesToLoad, function(module){
|
|
|
|
|
if (isString(module)) {
|
|
|
|
|
module = moduleRegistry[module];
|
|
|
|
|
}
|
|
|
|
|
if (isFunction(module) || isArray(module)) {
|
|
|
|
|
$injector(module);
|
|
|
|
|
} else {
|
|
|
|
|
assertArgFn(module, 'module');
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// instantiate $eager providers
|
|
|
|
|
// for perf we can't do forEach
|
|
|
|
|
for(var name in cache) {
|
|
|
|
|
var index = name.indexOf(providerSuffix);
|
|
|
|
|
if (index == name.length - providerSuffixLength && cache[name].$eager) {
|
|
|
|
|
$injector(name.substring(1, index));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $injector;
|
|
|
|
|
}
|