Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

Commit

Permalink
fix(injector): invoke config blocks for module after all providers
Browse files Browse the repository at this point in the history
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
  • Loading branch information
caitp committed May 2, 2014
1 parent 924ee6d commit c0b4e2d
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 12 deletions.
21 changes: 13 additions & 8 deletions src/auto/injector.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down
11 changes: 8 additions & 3 deletions src/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,

/**
Expand Down Expand Up @@ -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;
};
}
Expand Down
23 changes: 23 additions & 0 deletions test/auto/injectorSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
4 changes: 3 additions & 1 deletion test/loaderSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,16 @@ 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'] ],
['$provide', 'value', ['k', 'v'] ],
['$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']);
Expand Down

0 comments on commit c0b4e2d

Please sign in to comment.