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

Commit

Permalink
feat(angular.bootstrap): support deferred bootstrap
Browse files Browse the repository at this point in the history
This features enables tools like Batarang and test runners to
hook into angular's bootstrap process and sneak in more modules
into the DI registry which can replace or augment DI services for
the purpose of instrumentation or mocking out heavy dependencies.

If window.name contains prefix NG_DEFER_BOOTSTRAP! when
angular.bootstrap is called, the bootstrap process will be paused
until angular.resumeBootstrap is called.

angular.resumeBootstrap takes an optional array of modules that
should be added to the original list of modules that the app was
about to be bootstrapped with.
  • Loading branch information
juliemr authored and IgorMinar committed Mar 7, 2013
1 parent 485f104 commit 603fe0d
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 18 deletions.
50 changes: 33 additions & 17 deletions src/Angular.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ var /** holds major version number for IE or NaN for real browsers */
toString = Object.prototype.toString,


_angular = window.angular,
_angular = window.angular,
/** @name angular */
angular = window.angular || (window.angular = {}),
angularModule,
Expand Down Expand Up @@ -964,22 +964,38 @@ function angularInit(element, bootstrap) {
* @returns {AUTO.$injector} Returns the newly created injector for this app.
*/
function bootstrap(element, modules) {
element = jqLite(element);
modules = modules || [];
modules.unshift(['$provide', function($provide) {
$provide.value('$rootElement', element);
}]);
modules.unshift('ng');
var injector = createInjector(modules);
injector.invoke(
['$rootScope', '$rootElement', '$compile', '$injector', function(scope, element, compile, injector){
scope.$apply(function() {
element.data('$injector', injector);
compile(element)(scope);
});
}]
);
return injector;
var resumeBootstrapInternal = function() {
element = jqLite(element);
modules = modules || [];
modules.unshift(['$provide', function($provide) {
$provide.value('$rootElement', element);
}]);
modules.unshift('ng');
var injector = createInjector(modules);
injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
function(scope, element, compile, injector) {
scope.$apply(function() {
element.data('$injector', injector);
compile(element)(scope);
});
}]
);
return injector;
};

var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;

if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
return resumeBootstrapInternal();
}

window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
angular.resumeBootstrap = function(extraModules) {
forEach(extraModules, function(module) {
modules.push(module);
});
resumeBootstrapInternal();
};
}

var SNAKE_CASE_REGEXP = /[A-Z]/g;
Expand Down
74 changes: 73 additions & 1 deletion test/AngularSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -658,7 +658,7 @@ describe('angular', function() {
var element = jqLite('<div>{{1+2}}</div>');
var injector = angular.bootstrap(element);
expect(injector).toBeDefined();
expect(element.data('$injector')).toBe(injector);
expect(element.injector()).toBe(injector);
dealoc(element);
});

Expand All @@ -672,6 +672,78 @@ describe('angular', function() {
expect(element.html()).toBe('{{1+2}}');
dealoc(element);
});


describe('deferred bootstrap', function() {
var originalName = window.name,
element;

beforeEach(function() {
window.name = '';
element = jqLite('<div>{{1+2}}</div>');
});

afterEach(function() {
dealoc(element);
window.name = originalName;
});


it('should wait for extra modules', function() {
window.name = 'NG_DEFER_BOOTSTRAP!';
angular.bootstrap(element);

expect(element.html()).toBe('{{1+2}}');

angular.resumeBootstrap();

expect(element.html()).toBe('3');
expect(window.name).toEqual('');
});


it('should load extra modules', function() {
element = jqLite('<div>{{1+2}}</div>');
window.name = 'NG_DEFER_BOOTSTRAP!';

var bootstrapping = jasmine.createSpy('bootstrapping');
angular.bootstrap(element, [bootstrapping]);

expect(bootstrapping).not.toHaveBeenCalled();
expect(element.injector()).toBeUndefined();

angular.module('addedModule', []).value('foo', 'bar');
angular.resumeBootstrap(['addedModule']);

expect(bootstrapping).toHaveBeenCalledOnce();
expect(element.injector().get('foo')).toEqual('bar');
});


it('should not defer bootstrap without window.name cue', function() {
angular.bootstrap(element, []);
angular.module('addedModule', []).value('foo', 'bar');

expect(function() {
element.injector().get('foo');
}).toThrow('Unknown provider: fooProvider <- foo');

expect(element.injector().get('$http')).toBeDefined();
});


it('should restore the original window.name after bootstrap', function() {
window.name = 'NG_DEFER_BOOTSTRAP!my custom name';
angular.bootstrap(element);

expect(element.html()).toBe('{{1+2}}');

angular.resumeBootstrap();

expect(element.html()).toBe('3');
expect(window.name).toEqual('my custom name');
});
});
});


Expand Down

0 comments on commit 603fe0d

Please sign in to comment.