Skip to content
This repository was archived by the owner on Nov 21, 2025. It is now read-only.

Commit a012373

Browse files
committed
Merge pull request #16 from Shepless/master
Added ability to specify which module a constant is added to
2 parents 76c10bc + 476534e commit a012373

File tree

6 files changed

+286
-8
lines changed

6 files changed

+286
-8
lines changed

README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,41 @@ deferredBootstrapper.bootstrap({
7171
});
7272
```
7373

74+
## Attach constants to specific modules
75+
By default, any constants specified in the ```resolve``` object will be attached to the module specified in the ```module``` option. If you have a need to attach constants to different modules then this can be achieved by using ```moduleResolves```:
76+
77+
```js
78+
window.deferredBootstrapper.bootstrap({
79+
element: document.body,
80+
module: 'myApp',
81+
moduleResolves: [
82+
{
83+
module: 'myApp.settings',
84+
resolve: {
85+
CONSTANT_ONE: ['$http', function ($http) {
86+
return $http.get();
87+
}],
88+
CONSTANT_TWO: ['$http', function ($http) {
89+
return $http.get();
90+
}]
91+
}
92+
},
93+
{
94+
module: 'myApp.moreSettings',
95+
resolve: {
96+
CONSTANT_THREE: ['$http', function ($http) {
97+
return $http.get();
98+
}]
99+
}
100+
}
101+
]
102+
})
103+
```
104+
105+
In the above example, ```CONSTANT_ONE``` and ```CONSTANT_TWO``` will be added to the ```'myApp.settings'``` module and ```CONSTANT_THREE``` will be added to the ```'myApp.moreSettings'``` module. There are no limits on how many ```moduleResolve``` objects you create and also no limit on the number of constants per ```moduleResolve```
106+
107+
**Note** that only ```resolve``` or ```moduleResolves``` can be used - using both in the same configuration will throw an exception
108+
74109
## Custom injector modules
75110
By default, the injector that calls your resolve functions only provides the services from the AngularJS core module (ng). If you have a use case where you want to use one of your existing services to get configuration at bootstrap time, you can specify which modules should be made available and inject services from those modules in the resolve function. An example is below:
76111

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>Deferred Bootstrap Demo</title>
5+
<style type="text/css">
6+
#loading {
7+
display: none;
8+
}
9+
.deferred-bootstrap-loading #loading {
10+
display: block !important;
11+
}
12+
13+
#error {
14+
display: none;
15+
}
16+
.deferred-bootstrap-error #error {
17+
display: block !important;
18+
background: red;
19+
}
20+
</style>
21+
</head>
22+
<body>
23+
24+
<div id="loading">
25+
Loading...
26+
</div>
27+
28+
<div id="error">
29+
Could not load configuration!
30+
</div>
31+
32+
<div>
33+
Check the console for output...
34+
</div>
35+
36+
<script src="/bower_components/angular/angular.js"></script>
37+
<script src="/src/deferred-bootstrap.js"></script>
38+
<script>
39+
40+
window.deferredBootstrapper.bootstrap({
41+
element: window.document.body,
42+
module: 'demoApp',
43+
moduleResolves: [{
44+
module: 'demoApp.settings',
45+
resolve: {
46+
STARTUP_CONFIG: ['$http', function ($http) {
47+
return $http.get('/api/demo-config');
48+
}]
49+
}
50+
}]
51+
}).then(function () {
52+
angular.injector(['demoApp.settings']);
53+
});
54+
55+
angular.module('demoApp.settings', [])
56+
.config(function (STARTUP_CONFIG) {
57+
console.log('"demoApp.settings" in config() - STARTUP_CONFIG: ' + JSON.stringify(STARTUP_CONFIG));
58+
})
59+
.run(function (STARTUP_CONFIG) {
60+
console.log('"demoApp.settings" in run() - STARTUP_CONFIG: ' + JSON.stringify(STARTUP_CONFIG));
61+
});
62+
63+
angular.module('demoApp', [])
64+
.config(function ($injector) {
65+
var hasStartupConfig = $injector.has('STARTUP_CONFIG');
66+
console.log('"demoApp" in config() - "$injector.has("STARTUP_CONFIG")": ' + hasStartupConfig);
67+
})
68+
.run(function ($injector) {
69+
var hasStartupConfig = $injector.has('STARTUP_CONFIG');
70+
console.log('"demoApp" in run() - "$injector.has("STARTUP_CONFIG")": ' + hasStartupConfig);
71+
});
72+
73+
</script>
74+
</body>
75+
</html>

demo/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<ul>
99
<li><a href="simple.html">simple demo</a></li>
1010
<li><a href="multi.html">multi resolve demo</a></li>
11+
<li><a href="constant-to-specific-module.html">constant to specific modules</a></li>
1112
<li><a href="error.html">error in resolve demo</a></li>
1213
<li><a href="error-handler.html">error handler</a></li>
1314
</ul>

server.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ var express = require('express');
33
var app = express();
44
var server = require('http').createServer(app),
55
url = require('url'),
6+
path = require('path'),
67
fs = require('fs');
78

89
var getDemoConfig = function (req, res) {
@@ -26,9 +27,11 @@ app.configure(function () {
2627
app.use(express.bodyParser());
2728
app.use(express.methodOverride());
2829
app.use(express.errorHandler());
29-
app.use(express.static(__dirname));
30+
app.use(express.static(path.join(__dirname, 'demo')));
3031
app.use(app.router);
3132

33+
console.log(path.join(__dirname, 'demo'));
34+
3235
app.get('/api/demo-config', getDemoConfig);
3336
app.get('/api/demo-config-2', getDemoConfig);
3437
});

src/deferred-bootstrap.js

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,30 @@ function checkConfig (config) {
3535
if (!isString(config.module)) {
3636
throw new Error('\'config.module\' must be a string.');
3737
}
38-
if (!isObject(config.resolve)) {
39-
throw new Error('\'config.resolve\' must be an object.');
38+
if (config.resolve && config.moduleResolves) {
39+
throw new Error('Bootstrap configuration can contain either \'resolve\' or \'moduleResolves\' but not both');
4040
}
41+
if (config.resolve) {
42+
if (!isObject(config.resolve)) {
43+
throw new Error('\'config.resolve\' must be an object.');
44+
}
45+
}
46+
if (config.moduleResolves) {
47+
if (!isArray(config.moduleResolves)) {
48+
throw new Error('\'config.moduleResolves\' must be an array.');
49+
}
50+
}
51+
52+
forEach(config.moduleResolves, function (moduleResolve) {
53+
if (!moduleResolve.module) {
54+
throw new Error('A \'moduleResolve\' configuration item must contain a \'module\' name.');
55+
}
56+
57+
if (!isObject(moduleResolve.resolve)) {
58+
throw new Error('\'moduleResolve.resolve\' must be an object.');
59+
}
60+
});
61+
4162
if (angular.isDefined(config.onError) && !isFunction(config.onError)) {
4263
throw new Error('\'config.onError\' must be a function.');
4364
}
@@ -74,18 +95,21 @@ function bootstrap (configParam) {
7495
injectorModules = config.injectorModules || [],
7596
injector,
7697
promises = [],
77-
constantNames = [];
98+
constants = [];
7899

79100
bodyElement = angular.element(document.body);
80101

81102
addLoadingClass();
82103
checkConfig(config);
83104
injector = createInjector(injectorModules);
84105

85-
function callResolveFn (resolveFunction, constantName) {
106+
function callResolveFn (resolveFunction, constantName, moduleName) {
86107
var result;
87108

88-
constantNames.push(constantName);
109+
constants.push({
110+
name: constantName,
111+
moduleName: moduleName || module
112+
});
89113

90114
if (!isFunction(resolveFunction) && !isArray(resolveFunction)) {
91115
throw new Error('Resolve for \'' + constantName + '\' is not a valid dependency injection format.');
@@ -102,8 +126,11 @@ function bootstrap (configParam) {
102126

103127
function handleResults (results) {
104128
forEach(results, function (value, index) {
105-
var result = value && value.data ? value.data : value;
106-
angular.module(module).constant(constantNames[index], result);
129+
var result = value && value.data ? value.data : value,
130+
moduleName = constants[index].moduleName,
131+
constantName = constants[index].name;
132+
133+
angular.module(moduleName).constant(constantName, result);
107134
});
108135

109136
return doBootstrap(element, module);
@@ -118,6 +145,16 @@ function bootstrap (configParam) {
118145

119146
forEach(config.resolve, callResolveFn);
120147

148+
if (config.moduleResolves) {
149+
forEach(config.moduleResolves, function (moduleResolve, index) {
150+
forEach(moduleResolve.resolve, function (resolveFunction, constantName) {
151+
callResolveFn(resolveFunction, constantName, config.moduleResolves[index].module);
152+
});
153+
});
154+
} else {
155+
forEach(config.resolve, callResolveFn);
156+
}
157+
121158
return $q.all(promises).then(handleResults, handleError);
122159
}
123160

test/deferred-bootstrap.spec.js

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,75 @@ describe('deferredBootstrapper', function () {
8282
});
8383
});
8484

85+
itAsync('should allow constants to be added to a specified module', function (done) {
86+
var appModule ='app.module',
87+
constantsModuleOne = 'app.constants.one',
88+
constantsModuleTwo = 'app.constants.two';
89+
90+
angular.module(appModule, ['ng'])
91+
.config(function ($injector) {
92+
93+
expect($injector.has('CONSTANTS_ONE_CONFIG')).toBe(false);
94+
expect($injector.has('CONSTANTS_ONE_MORE_CONFIG')).toBe(false);
95+
expect($injector.has('CONSTANTS_TWO_CONFIG')).toBe(false);
96+
97+
done();
98+
});
99+
100+
angular.module(constantsModuleOne, []);
101+
angular.module(constantsModuleTwo, []);
102+
103+
bootstrap({
104+
element: bodyElement,
105+
module: appModule,
106+
moduleResolves: [
107+
{
108+
module: constantsModuleOne,
109+
resolve: {
110+
CONSTANTS_ONE_CONFIG: function ($q) {
111+
var deferred = $q.defer();
112+
113+
deferred.resolve('foo');
114+
115+
return deferred.promise;
116+
},
117+
CONSTANTS_ONE_MORE_CONFIG: function ($q) {
118+
var deferred = $q.defer();
119+
120+
deferred.resolve('foo');
121+
122+
return deferred.promise;
123+
}
124+
}
125+
},
126+
{
127+
module: constantsModuleTwo,
128+
resolve: {
129+
CONSTANTS_TWO_CONFIG: function ($q) {
130+
var deferred = $q.defer();
131+
132+
deferred.resolve('foo');
133+
134+
return deferred.promise;
135+
}
136+
}
137+
}
138+
]
139+
}).then(function () {
140+
var constantsOneInjector = angular.injector([constantsModuleOne]),
141+
constantsTwoInjector = angular.injector([constantsModuleTwo]);
142+
143+
expect(constantsOneInjector.has('CONSTANTS_ONE_CONFIG')).toBe(true);
144+
expect(constantsOneInjector.has('CONSTANTS_ONE_MORE_CONFIG')).toBe(true);
145+
expect(constantsOneInjector.has('CONSTANTS_TWO_CONFIG')).toBe(false);
146+
147+
expect(constantsTwoInjector.has('CONSTANTS_ONE_CONFIG')).toBe(false);
148+
expect(constantsTwoInjector.has('CONSTANTS_ONE_MORE_CONFIG')).toBe(false);
149+
expect(constantsTwoInjector.has('CONSTANTS_TWO_CONFIG')).toBe(true);
150+
done();
151+
});
152+
});
153+
85154
itAsync('should allow custom injector module(s) to be used to create the injector', function (done) {
86155
var customModuleName = 'custom.module';
87156
angular.module(customModuleName, ['ng'])
@@ -267,6 +336,21 @@ describe('deferredBootstrapper', function () {
267336
}).toThrow('\'config.module\' must be a string.');
268337
});
269338

339+
it('should throw if both resolve and moduleResolves are defined', function () {
340+
var config = {
341+
element: {},
342+
module: 'myModule',
343+
resolve: {
344+
CONST: function () {
345+
}
346+
},
347+
moduleResolves: []
348+
};
349+
expect(function () {
350+
checkConfig(config);
351+
}).toThrow('Bootstrap configuration can contain either \'resolve\' or \'moduleResolves\' but not both');
352+
});
353+
270354
it('should throw if resolve is not an object', function () {
271355
var config = {
272356
element: {},
@@ -278,6 +362,49 @@ describe('deferredBootstrapper', function () {
278362
}).toThrow('\'config.resolve\' must be an object.');
279363
});
280364

365+
it('should throw if moduleResolves is not an array', function () {
366+
var config = {
367+
element: {},
368+
module: 'myModule',
369+
moduleResolves: 1234
370+
};
371+
expect(function () {
372+
checkConfig(config);
373+
}).toThrow('\'config.moduleResolves\' must be an array.');
374+
});
375+
376+
it('should throw if a moduleResolve does not contain a module name', function () {
377+
var config = {
378+
element: {},
379+
module: 'myModule',
380+
moduleResolves: [{
381+
module: 'A.Test.Module',
382+
resolve: {}
383+
}, {
384+
resolve: {}
385+
}]
386+
};
387+
expect(function () {
388+
checkConfig(config);
389+
}).toThrow('A \'moduleResolve\' configuration item must contain a \'module\' name.');
390+
});
391+
392+
it('should throw if a moduleResolve does not contain a resolve block', function () {
393+
var config = {
394+
element: {},
395+
module: 'myModule',
396+
moduleResolves: [{
397+
module: 'A.Test.Module',
398+
resolve: {}
399+
}, {
400+
module: 'A.Test.Module'
401+
}]
402+
};
403+
expect(function () {
404+
checkConfig(config);
405+
}).toThrow('\'moduleResolve.resolve\' must be an object.');
406+
});
407+
281408
it('should throw if onError is defined but not a function', function () {
282409
var config = {
283410
element: {},

0 commit comments

Comments
 (0)