Thanks to visit codestin.com
Credit goes to github.com

Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
feat($injector): add support for non-string IDs
Previously, only strings could be used as identifiers for Angular services. This commit adds support
for using any value as identifier for an Angular service (e.g. used with `.provider()`,
`.factory()`, `.service()`, `.value()`, `.constant()`, `.decorator()`).

Among other things, non-string identifiers enable:
- Private services (without relying on naming conventions).
- Collision avoidance (without relying on custom prefixes and namespacing).
- Better toolability (e.g. autocomplete support for service identifiers).
- Better compression (i.e. strings can't be minified, non-string values could).

Identifiers for directives and filters are still restricted to string values, since they need to be
referenced in HTML (text).

--
For services with string IDs, the corresponding provider ID is constructed by appending `Provider`
to the service ID. For services with non-string IDs, the corresponding provider has the exact same
ID (it is the context that determines if a service or a provider should be injected).

E.g.:
```js
var bar = {};

angular.
  module('myModule', []).

  provider('foo' /* string ID     */, {$get: function () { return 'FOO'; }}).
  provider( bar  /* non-string ID */, {$get: function () { return 'BAR'; }}).

  config(['fooProvider', function (fooProvider) {
    // `foo` provider injected (because we are in config block)
  }]).
  run(['foo', function (foo) {
    // `foo` service injected (because we are in run block)
  }]).

  config([bar, function (barProvider) {
    // `bar` provider injected (because we are in config block)
    // (even though we used the same identifier (`bar`) that we will use in the run block)
  }]).
  run([bar, function (bar) {
    // `bar` service injected (because we are in run block)
  }]);
```

--
This change is backwards compatible (afaict).

Fixes #10347
  • Loading branch information
gkalpak committed Aug 6, 2016
commit 32bde2d0bbb0f5f7465aa3e73011928b7621c824
26 changes: 0 additions & 26 deletions docs/content/error/$injector/itkn.ngdoc

This file was deleted.

113 changes: 67 additions & 46 deletions src/auto/injector.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
* Implicit module which gets automatically added to each {@link auto.$injector $injector}.
*/

var PROVIDER_ID_SUFFIX = 'Provider';
var ARROW_ARG = /^([^\(]+?)=>/;
var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
var FN_ARG_SPLIT = /,/;
Expand Down Expand Up @@ -129,6 +130,14 @@ function annotate(fn, strictDi, name) {
return $inject;
}

function getProviderId(id) {
return !isString(id) ? id : id + PROVIDER_ID_SUFFIX;
}

function stringifyIdForError(id, suffix) {
return isString(id) ? id : id + suffix;
}

///////////////////////////////////////

/**
Expand Down Expand Up @@ -647,10 +656,9 @@ function annotate(fn, strictDi, name) {
function createInjector(modulesToLoad, strictDi) {
strictDi = (strictDi === true);
var INSTANTIATING = {},
providerSuffix = 'Provider',
path = [],
loadedModules = new HashMap(null, true),
providerCache = {
providerCache = new HashMap({
$provide: {
provider: supportObject(provider),
factory: supportObject(factory),
Expand All @@ -659,24 +667,22 @@ function createInjector(modulesToLoad, strictDi) {
constant: supportObject(constant),
decorator: decorator
}
},
providerInjector = (providerCache.$injector =
createInternalInjector(providerCache, function(serviceName, caller) {
if (angular.isString(caller)) {
path.push(caller);
}
}),
instanceCache = new HashMap(),
providerInjector =
createInternalInjector(providerCache, function(serviceName) {
throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
})),
instanceCache = {},
}, ' (provider)'),
protoInstanceInjector =
createInternalInjector(instanceCache, function(serviceName, caller) {
var provider = providerInjector.get(serviceName + providerSuffix, caller);
return instanceInjector.invoke(
provider.$get, provider, undefined, serviceName);
createInternalInjector(instanceCache, function(serviceName) {
var provider = providerInjector.get(getProviderId(serviceName));
return instanceInjector.invoke(provider.$get, provider);
}),
instanceInjector = protoInstanceInjector;

providerCache['$injector' + providerSuffix] = { $get: valueFn(protoInstanceInjector) };
providerCache.put('$injector', providerInjector);
providerCache.put(getProviderId('$injector'), {$get: valueFn(protoInstanceInjector)});

var runBlocks = loadModules(modulesToLoad);
instanceInjector = protoInstanceInjector.get('$injector');
instanceInjector.strictDi = strictDi;
Expand All @@ -690,7 +696,7 @@ function createInjector(modulesToLoad, strictDi) {

function supportObject(delegate) {
return function(key, value) {
if (isObject(key)) {
if ((arguments.length === 1) && isObject(key)) {
forEach(key, reverseParams(delegate));
} else {
return delegate(key, value);
Expand All @@ -706,7 +712,10 @@ function createInjector(modulesToLoad, strictDi) {
if (!provider_.$get) {
throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
}
return (providerCache[name + providerSuffix] = provider_);

providerCache.put(getProviderId(name), provider_);

return provider_;
}

function enforceReturnValue(name, factory) {
Expand Down Expand Up @@ -737,12 +746,12 @@ function createInjector(modulesToLoad, strictDi) {

function constant(name, value) {
assertNotHasOwnProperty(name, 'constant');
providerCache[name] = value;
instanceCache[name] = value;
providerCache.put(name, value);
instanceCache.put(name, value);
}

function decorator(serviceName, decorFn) {
var origProvider = providerInjector.get(serviceName + providerSuffix),
var origProvider = providerInjector.get(getProviderId(serviceName)),
orig$get = origProvider.$get;

origProvider.$get = function() {
Expand Down Expand Up @@ -805,29 +814,45 @@ function createInjector(modulesToLoad, strictDi) {
// internal Injector
////////////////////////////////////

function createInternalInjector(cache, factory) {
function createInternalInjector(cache, factory, displayNameSuffix) {
if (!isString(displayNameSuffix)) {
displayNameSuffix = '';
}

function getService(serviceName, caller) {
if (cache.hasOwnProperty(serviceName)) {
if (cache[serviceName] === INSTANTIATING) {
throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
serviceName + ' <- ' + path.join(' <- '));
}
return cache[serviceName];
} else {
try {
path.unshift(serviceName);
cache[serviceName] = INSTANTIATING;
cache[serviceName] = factory(serviceName, caller);
return cache[serviceName];
} catch (err) {
if (cache[serviceName] === INSTANTIATING) {
delete cache[serviceName];
var hasCaller = isDefined(caller);
var hadInstance = cache.has(serviceName);
var instance;

if (hasCaller) {
path.unshift(stringifyIdForError(caller, displayNameSuffix));
}
path.unshift(stringifyIdForError(serviceName, displayNameSuffix));

try {
if (hadInstance) {
instance = cache.get(serviceName);

if (instance === INSTANTIATING) {
throw $injectorMinErr('cdep', 'Circular dependency found: {0}', path.join(' <- '));
}
throw err;
} finally {
path.shift();

return instance;
} else {
cache.put(serviceName, INSTANTIATING);

instance = factory(serviceName);
cache.put(serviceName, instance);

return instance;
}
} finally {
if (!hadInstance && (cache.get(serviceName) === INSTANTIATING)) {
cache.remove(serviceName);
}

path.shift();
if (hasCaller) path.shift();
}
}

Expand All @@ -838,12 +863,8 @@ function createInjector(modulesToLoad, strictDi) {

for (var i = 0, length = $inject.length; i < length; i++) {
var key = $inject[i];
if (typeof key !== 'string') {
throw $injectorMinErr('itkn',
'Incorrect injection token! Expected service name as string, got {0}', key);
}
args.push(locals && locals.hasOwnProperty(key) ? locals[key] :
getService(key, serviceName));
var localsHasKey = locals && isString(key) && locals.hasOwnProperty(key);
args.push(localsHasKey ? locals[key] : getService(key, serviceName));
}
return args;
}
Expand Down Expand Up @@ -901,7 +922,7 @@ function createInjector(modulesToLoad, strictDi) {
get: getService,
annotate: createInjector.$$annotate,
has: function(name) {
return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
return cache.has(name) || providerCache.has(getProviderId(name));
}
};
}
Expand Down
Loading