Skip to content
This repository was archived by the owner on Nov 21, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 26 additions & 15 deletions src/deferred-bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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.');
}
Expand All @@ -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 () {
Expand All @@ -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 = [];

Expand All @@ -69,23 +78,26 @@ 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 {
throw new Error('Resolve function for \'' + constantName + '\' must return a promise.');
}
}

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);
Expand All @@ -107,7 +119,6 @@ function bootstrap (configParam) {

}

// publish external API
window.deferredBootstrapper = {
bootstrap: bootstrap
};
};
84 changes: 73 additions & 11 deletions test/deferred-bootstrap.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -11,7 +11,7 @@ function itAsync(title, func) {

func(done);

waitsFor(function() {
waitsFor(function () {
return finished === true;
});
});
Expand All @@ -30,7 +30,7 @@ describe('deferredBootstrapper', function () {
bodyElement;
var APP_NAME = 'testApp';

beforeEach(function() {
beforeEach(function () {
bootstrap = window.deferredBootstrapper.bootstrap;
bodyElement = window.document.body;
});
Expand Down Expand Up @@ -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({
Expand Down Expand Up @@ -148,7 +206,8 @@ describe('deferredBootstrapper', function () {
element: {},
module: 'myModule',
resolve: {
CONST: function () {}
CONST: function () {
}
}
};
checkConfig(config);
Expand All @@ -166,7 +225,8 @@ describe('deferredBootstrapper', function () {
element: {},
module: [],
resolve: {
CONST: function () {}
CONST: function () {
}
}
};
expect(function () {
Expand All @@ -190,7 +250,8 @@ describe('deferredBootstrapper', function () {
element: {},
module: 'myModule',
resolve: {
CONST: function () {}
CONST: function () {
}
},
onError: 'bla'
};
Expand All @@ -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);
});
Expand Down