diff --git a/src/deferred-bootstrap.js b/src/deferred-bootstrap.js index 44afcfb..187363c 100644 --- a/src/deferred-bootstrap.js +++ b/src/deferred-bootstrap.js @@ -2,12 +2,12 @@ var isObject = angular.isObject, isFunction = angular.isFunction, + isArray = angular.isArray, isString = angular.isString, forEach = angular.forEach, + ngInjector = angular.injector(['ng']), + $q = ngInjector.get('$q'), bodyElement, - injector = angular.injector(['ng']), - $q = injector.get('$q'), - $http = injector.get('$http'), loadingClass = 'deferred-bootstrap-loading', errorClass = 'deferred-bootstrap-error'; @@ -24,11 +24,11 @@ function addErrorClass() { bodyElement.addClass(errorClass); } -function isPromise (value) { +function isPromise(value) { return isObject(value) && isFunction(value.then); } -function checkConfig (config) { +function checkConfig(config) { if (!isObject(config)) { throw new Error('Bootstrap configuration must be an object.'); } @@ -43,7 +43,15 @@ function checkConfig (config) { } } -function doBootstrap (element, module) { +function createInjector(injectorModules) { + if (!isArray(injectorModules)) { + return (injectorModules === 'ng') ? ngInjector : angular.injector([injectorModules]); + } else { + return (injectorModules.length === 1 && injectorModules[0] === 'ng') ? ngInjector : angular.injector(injectorModules); + } +} + +function doBootstrap(element, module) { var deferred = $q.defer(); angular.element(document).ready(function () { @@ -56,11 +64,12 @@ function doBootstrap (element, module) { return deferred.promise; } -function bootstrap (configParam) { - +function bootstrap(configParam) { var config = configParam || {}, element = config.element, module = config.module, + injectorModules = config.injectorModules || ['ng'], + injector, promises = [], constantNames = []; @@ -69,15 +78,18 @@ function bootstrap (configParam) { addLoadingClass(); checkConfig(config); - function callResolveFn (resolveFunction, constantName) { + function callResolveFn(resolveFunction, constantName) { var result; constantNames.push(constantName); - if (!isFunction(resolveFunction)) { - throw new Error('Resolve for \'' + constantName + '\' is not a function.'); + + if (!isFunction(resolveFunction) && !isArray(resolveFunction)) { + throw new Error('Resolve for \'' + constantName + '\' is not a valid dependency injection format.'); } - result = resolveFunction($http, $q, injector); + injector = createInjector(injectorModules); + result = injector.instantiate(resolveFunction); + if (isPromise(result)) { promises.push(result); } else { @@ -85,7 +97,7 @@ function bootstrap (configParam) { } } - function handleResults (results) { + function handleResults(results) { forEach(results, function (value, index) { var result = value && value.data ? value.data : value; angular.module(module).constant(constantNames[index], result); @@ -107,7 +119,6 @@ function bootstrap (configParam) { } -// publish external API window.deferredBootstrapper = { bootstrap: bootstrap -}; +}; \ No newline at end of file diff --git a/test/deferred-bootstrap.spec.js b/test/deferred-bootstrap.spec.js index 9a685d5..394ff3d 100644 --- a/test/deferred-bootstrap.spec.js +++ b/test/deferred-bootstrap.spec.js @@ -2,7 +2,7 @@ // A jasmine 2.0 -like interface for async tests function itAsync(title, func) { - it(title, function() { + it(title, function () { var finished = false; function done() { @@ -11,7 +11,7 @@ function itAsync(title, func) { func(done); - waitsFor(function() { + waitsFor(function () { return finished === true; }); }); @@ -30,7 +30,7 @@ describe('deferredBootstrapper', function () { bodyElement; var APP_NAME = 'testApp'; - beforeEach(function() { + beforeEach(function () { bootstrap = window.deferredBootstrapper.bootstrap; bodyElement = window.document.body; }); @@ -76,13 +76,71 @@ describe('deferredBootstrapper', function () { expect(isPromise(promise)).toBe(true); promise.then(function (result) { - expect(result).toBe(true); + expect(result).toBe(true); - done(); - }); + done(); + }); + }); + + itAsync('should allow custom injector module(s) to be used to create the injector', function (done) { + var customModuleName = 'custom.module'; + angular.module(customModuleName, ['ng']) + .service('CustomService', ['$q', function ($q) { + this.get = function () { + var deferred = $q.defer(); + + deferred.resolve('foo'); + + return deferred.promise; + }; + }]); + + var promise = bootstrap({ + element: bodyElement, + module: APP_NAME, + injectorModules: customModuleName, + resolve: { + CONFIG: ['CustomService', function (CustomService) { + return CustomService.get(); + }] + } + }); + + expect(isPromise(promise)).toBe(true); + + promise.then(function (result) { + expect(result).toBe(true); + + done(); + }); + }); + + itAsync('should use the default ngInjector if "ng" is specified as the injectorModules config option', function (done) { + var promise = bootstrap({ + element: bodyElement, + module: APP_NAME, + injectorModules: ['ng'], + resolve: { + CONFIG: function ($http, $q) { + var deferred = $q.defer(); + + deferred.resolve('foo'); + + return deferred.promise; + } + } + }); + + expect(isPromise(promise)).toBe(true); + + promise.then(function (result) { + expect(result).toBe(true); + + done(); + }); }); - describe('CSS class handling', function() { + describe('CSS class handling', function () { itAsync('should add loading class immediately and remove it when resolved', function (done) { var promise = bootstrap({ @@ -148,7 +206,8 @@ describe('deferredBootstrapper', function () { element: {}, module: 'myModule', resolve: { - CONST: function () {} + CONST: function () { + } } }; checkConfig(config); @@ -166,7 +225,8 @@ describe('deferredBootstrapper', function () { element: {}, module: [], resolve: { - CONST: function () {} + CONST: function () { + } } }; expect(function () { @@ -190,7 +250,8 @@ describe('deferredBootstrapper', function () { element: {}, module: 'myModule', resolve: { - CONST: function () {} + CONST: function () { + } }, onError: 'bla' }; @@ -205,7 +266,8 @@ describe('deferredBootstrapper', function () { it('should check if object is a promise', function () { var promise = { - then: function () {} + then: function () { + } }; expect(isPromise(promise)).toBe(true); });