diff --git a/src/Angular.js b/src/Angular.js index 5de57075eb5c..5d9d2e12fffe 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -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, @@ -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; diff --git a/test/AngularSpec.js b/test/AngularSpec.js index cc752b6c00bd..7f1ab8a32c26 100644 --- a/test/AngularSpec.js +++ b/test/AngularSpec.js @@ -658,7 +658,7 @@ describe('angular', function() { var element = jqLite('
{{1+2}}
'); 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('
{{1+2}}
'); + }); + + 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('
{{1+2}}
'); + 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'); + }); + }); });