aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJulie2013-02-21 11:55:16 -0800
committerIgor Minar2013-03-06 16:25:21 -0800
commit13968343d4fe938edc84d39cb2d86e233f97ecda (patch)
tree40d3cf760467bea4291635b49ad7cf9e6184c2aa
parent4c428121b99ec08a561e330262184626d262619c (diff)
downloadangular.js-13968343d4fe938edc84d39cb2d86e233f97ecda.tar.bz2
feat(angular.bootstrap): support deferred bootstrap
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. Conflicts: src/Angular.js
-rw-r--r--src/Angular.js48
-rw-r--r--test/AngularSpec.js74
2 files changed, 105 insertions, 17 deletions
diff --git a/src/Angular.js b/src/Angular.js
index 93e4f6af..20c032c1 100644
--- a/src/Angular.js
+++ b/src/Angular.js
@@ -944,22 +944,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;
diff --git a/test/AngularSpec.js b/test/AngularSpec.js
index c535bf12..28b2205f 100644
--- a/test/AngularSpec.js
+++ b/test/AngularSpec.js
@@ -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);
});
@@ -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');
+ });
+ });
});