mirror of
https://github.com/zhigang1992/angular.js.git
synced 2026-01-12 22:45:52 +08:00
fix(injector): invoke config blocks for module after all providers
This change ensures that a module's config blocks are always invoked after all of its providers are
registered.
BREAKING CHANGE:
Previously, config blocks would be able to control behaviour of provider registration, due to being
invoked prior to provider registration. Now, provider registration always occurs prior to configuration
for a given module, and therefore config blocks are not able to have any control over a providers
registration.
Example:
Previously, the following:
angular.module('foo', [])
.provider('$rootProvider', function() {
this.$get = function() { ... }
})
.config(function($rootProvider) {
$rootProvider.dependentMode = "B";
})
.provider('$dependentProvider', function($rootProvider) {
if ($rootProvider.dependentMode === "A") {
this.$get = function() {
// Special mode!
}
} else {
this.$get = function() {
// something else
}
}
});
would have "worked", meaning behaviour of the config block between the registration of "$rootProvider"
and "$dependentProvider" would have actually accomplished something and changed the behaviour of the
app. This is no longer possible within a single module.
Fixes #7139
Closes #7147
This commit is contained in:
@@ -693,22 +693,27 @@ function createInjector(modulesToLoad, strictDi) {
|
||||
// Module Loading
|
||||
////////////////////////////////////
|
||||
function loadModules(modulesToLoad){
|
||||
var runBlocks = [], moduleFn, invokeQueue, i, ii;
|
||||
var runBlocks = [], moduleFn, invokeQueue;
|
||||
forEach(modulesToLoad, function(module) {
|
||||
if (loadedModules.get(module)) return;
|
||||
loadedModules.put(module, true);
|
||||
|
||||
function runInvokeQueue(queue) {
|
||||
var i, ii;
|
||||
for(i = 0, ii = queue.length; i < ii; i++) {
|
||||
var invokeArgs = queue[i],
|
||||
provider = providerInjector.get(invokeArgs[0]);
|
||||
|
||||
provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (isString(module)) {
|
||||
moduleFn = angularModule(module);
|
||||
runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
|
||||
|
||||
for(invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) {
|
||||
var invokeArgs = invokeQueue[i],
|
||||
provider = providerInjector.get(invokeArgs[0]);
|
||||
|
||||
provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
|
||||
}
|
||||
runInvokeQueue(moduleFn._invokeQueue);
|
||||
runInvokeQueue(moduleFn._configBlocks);
|
||||
} else if (isFunction(module)) {
|
||||
runBlocks.push(providerInjector.invoke(module));
|
||||
} else if (isArray(module)) {
|
||||
|
||||
@@ -99,15 +99,19 @@ function setupModuleLoader(window) {
|
||||
/** @type {!Array.<Array.<*>>} */
|
||||
var invokeQueue = [];
|
||||
|
||||
/** @type {!Array.<Function>} */
|
||||
var configBlocks = [];
|
||||
|
||||
/** @type {!Array.<Function>} */
|
||||
var runBlocks = [];
|
||||
|
||||
var config = invokeLater('$injector', 'invoke');
|
||||
var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
|
||||
|
||||
/** @type {angular.Module} */
|
||||
var moduleInstance = {
|
||||
// Private state
|
||||
_invokeQueue: invokeQueue,
|
||||
_configBlocks: configBlocks,
|
||||
_runBlocks: runBlocks,
|
||||
|
||||
/**
|
||||
@@ -299,9 +303,10 @@ function setupModuleLoader(window) {
|
||||
* @param {String=} insertMethod
|
||||
* @returns {angular.Module}
|
||||
*/
|
||||
function invokeLater(provider, method, insertMethod) {
|
||||
function invokeLater(provider, method, insertMethod, queue) {
|
||||
if (!queue) queue = invokeQueue;
|
||||
return function() {
|
||||
invokeQueue[insertMethod || 'push']([provider, method, arguments]);
|
||||
queue[insertMethod || 'push']([provider, method, arguments]);
|
||||
return moduleInstance;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -308,6 +308,29 @@ describe('injector', function() {
|
||||
expect(log).toEqual('abABCD');
|
||||
});
|
||||
|
||||
it('should execute own config blocks after all own providers are invoked', function() {
|
||||
var log = '';
|
||||
angular.module('a', ['b'])
|
||||
.config(function($aProvider) {
|
||||
log += 'aConfig;';
|
||||
})
|
||||
.provider('$a', function() {
|
||||
log += '$aProvider;';
|
||||
this.$get = function() {};
|
||||
});
|
||||
angular.module('b', [])
|
||||
.config(function($bProvider) {
|
||||
log += 'bConfig;';
|
||||
})
|
||||
.provider('$b', function() {
|
||||
log += '$bProvider;';
|
||||
this.$get = function() {};
|
||||
});
|
||||
|
||||
createInjector(['a']);
|
||||
expect(log).toBe('$bProvider;bConfig;$aProvider;aConfig;');
|
||||
});
|
||||
|
||||
describe('$provide', function() {
|
||||
|
||||
it('should throw an exception if we try to register a service called "hasOwnProperty"', function() {
|
||||
|
||||
@@ -46,7 +46,6 @@ describe('module loader', function() {
|
||||
expect(myModule.requires).toEqual(['other']);
|
||||
expect(myModule._invokeQueue).toEqual([
|
||||
['$provide', 'constant', ['abc', 123] ],
|
||||
['$injector', 'invoke', ['config'] ],
|
||||
['$provide', 'provider', ['sk', 'sv'] ],
|
||||
['$provide', 'factory', ['fk', 'fv'] ],
|
||||
['$provide', 'service', ['a', 'aa'] ],
|
||||
@@ -54,6 +53,9 @@ describe('module loader', function() {
|
||||
['$filterProvider', 'register', ['f', 'ff'] ],
|
||||
['$compileProvider', 'directive', ['d', 'dd'] ],
|
||||
['$controllerProvider', 'register', ['ctrl', 'ccc']],
|
||||
]);
|
||||
expect(myModule._configBlocks).toEqual([
|
||||
['$injector', 'invoke', ['config'] ],
|
||||
['$injector', 'invoke', ['init2'] ]
|
||||
]);
|
||||
expect(myModule._runBlocks).toEqual(['runBlock']);
|
||||
|
||||
Reference in New Issue
Block a user