Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 5d8528c

Browse files
committed
docs(module): Describe module loading
1 parent 80edcad commit 5d8528c

File tree

1 file changed

+258
-0
lines changed

1 file changed

+258
-0
lines changed

docs/content/guide/module.ngdoc

+258
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
@ngdoc overview
2+
@name Developer Guide: Modules
3+
@description
4+
5+
# What is a Module?
6+
7+
Most applications have a main method which instantiates, wires, and bootstraps the application.
8+
Angular apps don't have a main method, instead the modules serves the purpose of declaratively
9+
specifying how an application should be bootstrapped. There are several advantages to this
10+
approach:
11+
12+
* The process is more declarative which is easier to understand
13+
* In unit-testing there is no need to load all modules, which may aid in writing unit-tests.
14+
* Additional modules can be loaded in scenario tests, which can override some of the
15+
configuration and help end-to-end test the application
16+
* Third party code can be packaged as reusable modules.
17+
* The modules can be loaded in any/parallel order (due to delayed nature of module execution).
18+
19+
20+
# The Basics
21+
22+
Ok i am in a hurry how do i get a Hello World module working?
23+
24+
Important things to notice:
25+
26+
* {@link api/angular.Module Module} API
27+
* Notice the reference to the `myApp` module in the `<html ng:app="myApp">`, it is what
28+
bootstraps the app using your module.
29+
30+
<doc:example module='simpleApp'>
31+
<doc:source>
32+
<script>
33+
// declare a module
34+
var simpleAppModule = angular.module('simpleApp', []);
35+
36+
// configure the module.
37+
// in this example we will create a greeting filter
38+
simpleAppModule.filter('greet', function() {
39+
return function(name) {
40+
return 'Hello, ' + name + '!';
41+
};
42+
});
43+
44+
</script>
45+
<div>
46+
{{ 'World' | greet }}
47+
</div>
48+
</doc:source>
49+
</doc:example>
50+
51+
52+
53+
# Recommended Setup
54+
55+
While the example above is simple, it will not scale to large applications. Instead we recommend
56+
that you break your application to multiple modules like this:
57+
58+
* A service module, for service declaration
59+
* A directive module, for directive declaration
60+
* A filter module, for filter declaration
61+
* And an application level module which depends on the above modules, and which has
62+
initialization code.
63+
64+
The reason for this breakup is that in your tests, it is often necessary to ignore the
65+
initialization code, which tends to be difficult to test. By putting it into separate module it
66+
can be easily ignored in tests. The tests can also be more focused by only loading the modules
67+
which are relevant to tests.
68+
69+
The above is only a suggestion, so feel free to tailor it to your needs.
70+
71+
<doc:example module='xmpl'>
72+
<doc:source>
73+
<script>
74+
angular.module('xmpl.service', []).
75+
value('greeter', {
76+
salutation: 'Hello',
77+
localize: function(localization) {
78+
this.salutation = localization.salutation;
79+
},
80+
greet: function(name) {
81+
return this.salutation + ' ' + name + '!';
82+
}
83+
}).
84+
value('user', {
85+
load: function(name) {
86+
this.name = name;
87+
}
88+
});
89+
90+
angular.module('xmpl.directive', []);
91+
92+
angular.module('xmpl.filter', []);
93+
94+
angular.module('xmpl', ['xmpl.service', 'xmpl.directive', 'xmpl.filter']).
95+
run(function(greeter, user) {
96+
// This is effectively part of the main method initialization code
97+
greeter.localize({
98+
salutation: 'Bonjour'
99+
});
100+
user.load('World');
101+
})
102+
103+
104+
// A Controller for your app
105+
var XmplController = function($scope, greeter, user) {
106+
$scope.greeting = greeter.greet(user.name);
107+
}
108+
</script>
109+
<div ng-controller="XmplController">
110+
{{ greeting }}!
111+
</div>
112+
</doc:source>
113+
</doc:example>
114+
115+
116+
117+
# Module Loading & Dependencies
118+
119+
A module is a collection of configuration and run block which get applied to the application
120+
during the bootstrap process. In its simplest form the module consist of collection of two kinds
121+
of blocks:
122+
123+
1. **Configuration blocks** - get executed during the provider registrations and configuration
124+
phase. Only providers and constants can be injected into configuration blocks. This is to
125+
prevent accidental instantiation of services before they have been fully configured.
126+
2. **Run blocks** - get executed after the injector is created and are used to kickstart the
127+
application. Only instances and constants can be injected into run blocks. This is to prevent
128+
further system configuration during application run time.
129+
130+
<pre>
131+
angular.module('myModule', []).
132+
config(function(injectables) { // provider-injector
133+
// This is an example of config block.
134+
// You can have as many of these as you want.
135+
// You can only inject Providers (not instances)
136+
// into the config blocks.
137+
}).
138+
run(function(injectables) { // instance-injector
139+
// This is an example of a run block.
140+
// You can have as many of these as you want.
141+
// You can only inject instances (not Providers)
142+
// int the run blocks
143+
});
144+
</pre>
145+
146+
## Configuration Blocks
147+
148+
There are some convenience methods on the module which are equivalent to the config block. For
149+
example:
150+
151+
<pre>
152+
angular.module('myModule', []).
153+
value('a', 123).
154+
factory('a', function() { return 123; }).
155+
directive('directiveName', ...).
156+
filter('filterName', ...);
157+
158+
// is same as
159+
160+
angular.module('myModule', []).
161+
config(function($provide, $compileProvider, $filterProvider) {
162+
$provide.value('a', 123)
163+
$provide.factory('a', function() { return 123; })
164+
$compileProvider.directive('directiveName', ...).
165+
$filterProvider.register('filterName', ...);
166+
});
167+
</pre>
168+
169+
The configuration blocks get applied in the order in which they are registered. The only exception
170+
to it are constant definitions, which are placed at the beginning of all configuration blocks.
171+
172+
## Run Blocks
173+
174+
Run blocks are the closest thing in Angular to the main method. A run block is the code which
175+
needs to run to kickstart the application. It is executed after all of the service have been
176+
configured and the injector has been created. Run blocks typically contain code which is hard
177+
to unit-test, and for this reason should be declared in isolated modules, so that they can be
178+
ignored in the unit-tests.
179+
180+
## Dependencies
181+
182+
Modules can list other modules as their dependencies. Depending on a module implies that required
183+
module needs to be loaded before the requiring module is loaded. In other words the configuration
184+
blocks of the required modules execute before the configuration blocks or the requiring module.
185+
The same is true for the run blocks. Each module can only be loaded once, even if multiple other
186+
modules require it.
187+
188+
## Asynchronous Loading
189+
190+
Modules are a way of managing $injector configuration, and have nothing to do with loading of
191+
scripts into a VM. There are existing projects which deal with script loading, which may be used
192+
with Angular. Because modules do nothing at load time they can be loaded into the VM in any order
193+
and thus script loaders can take advantage of this property and paralyze the loading process.
194+
195+
196+
# Unit Testing
197+
198+
In its simplest form a unit-test is a way of instantiating a subset of the application in test and
199+
then applying a stimulus to it. It is important to realize that each module can only be loaded
200+
once per injector. Typically an app has only one injector. But in tests, each test has its own
201+
injector, which means that the modules are loaded multiple times per VM. Properly structured
202+
modules can help with unit testing, as in this example:
203+
204+
In all of these examples we are going to assume this module definition:
205+
<pre>
206+
angular.module('greetMod', []).
207+
208+
factory('alert', function($window) {
209+
return function(text) {
210+
$window.alert(text);
211+
}
212+
}).
213+
214+
value('salutation', 'Hello').
215+
216+
factory('greet', function(alert, salutation) {
217+
return function(name) {
218+
alert(salutation + ' ' + name + '!');
219+
}
220+
});
221+
</pre>
222+
223+
Let's write some tests:
224+
<pre>
225+
describe('myApp', function() {
226+
// load the application relevant modules then load a special
227+
// test module which overrides the $window with mock version,
228+
// so that calling window.alert() will not block the test
229+
// runner with a real alert box. This is an example of overriding
230+
// configuration information in tests.
231+
beforeEach(module('greetMod', function($provide) {
232+
$provide.value('$window', {
233+
alert: jasmine.createSpy('alert')
234+
});
235+
});
236+
237+
// The inject() will create the injector and inject the greet and
238+
// $window into the tests. The test need not concern itself with
239+
// wiring of the application, only with testing it.
240+
it('should alert on $window', inject(function(greet, $window) {
241+
greet('World');
242+
expect($window.alert).toHaveBeenCalledWith('Hello World!');
243+
}));
244+
245+
// this is another way of overriding configuration in the
246+
// tests using an inline module and inject methods.
247+
it('should alert using the alert service', function() {
248+
var alertSpy = jasmine.createSpy('alert');
249+
module(function($provide) {
250+
$provide.value('alert', alertSpy);
251+
});
252+
inject(function(greet) {
253+
greet('World');
254+
expect(alertSpy).toHaveBeenCalledWith('World');
255+
});
256+
});
257+
});
258+
</pre>

0 commit comments

Comments
 (0)