docs(guide/module): improve clarity

This commit is contained in:
Brian Ford
2014-03-03 12:51:56 -08:00
parent 2206b99359
commit d07101dec0

View File

@@ -4,27 +4,27 @@
# What is a Module?
Most applications have a main method which instantiates, wires, and bootstraps the application.
You can think of a module as a container for the different parts of your app controllers,
services, filters, directives, etc.
# Why?
Most applications have a main method that instantiates and wires together the different parts of
the application.
Angular apps don't have a main method. Instead modules declaratively specify how an application
should be bootstrapped. There are several advantages to this approach:
* The process is more declarative which is easier to understand
* In unit-testing there is no need to load all modules, which may aid in writing unit-tests.
* Additional modules can be loaded in scenario tests, which can override some of the
configuration and help end-to-end test the application
* Third party code can be packaged as reusable modules.
* The modules can be loaded in any/parallel order (due to delayed nature of module execution).
* The declarative process is easier to understand.
* You can package code as reusable modules.
* The modules can be loaded in any order (or even in parallel) because modules delay execution.
* Unit tests only have to load relevant modules, which keeps them fast.
* End-to-end tests can use modules to override configuration.
# The Basics
Ok, I'm in a hurry. How do I get a Hello World module working?
Important things to notice:
* {@link angular.Module Module} API
* Notice the reference to the `myApp` module in the `<html ng-app="myApp">`, it is what
bootstraps the app using your module.
I'm in a hurry. How do I get a Hello World module working?
<example module='myApp'>
<file name="index.html">
@@ -47,6 +47,13 @@ Important things to notice:
</file>
</example>
Important things to notice:
* The {@link angular.Module Module} API
* The reference to `myApp` module in `<html ng-app="myApp">`.
This is what bootstraps the app using your module.
* The empty array in `angular.module('myApp', [])`.
This array is the list of modules `myApp` depends on.
# Recommended Setup
@@ -54,18 +61,15 @@ Important things to notice:
While the example above is simple, it will not scale to large applications. Instead we recommend
that you break your application to multiple modules like this:
* A service module, for service declaration
* A directive module, for directive declaration
* A filter module, for filter declaration
* And an application level module which depends on the above modules, and which has
* A module for each feature
* A module for each reusable component (especially directives and filters)
* And an application level module which depends on the above modules and contains any
initialization code.
The reason for this breakup is that in your tests, it is often necessary to ignore the
initialization code, which tends to be difficult to test. By putting it into a separate module it
can be easily ignored in tests. The tests can also be more focused by only loading the modules
that are relevant to tests.
We've also written a document on how we organize large apps at Google and on how to write
reusable components.
The above is only a suggestion, so feel free to tailor it to your needs.
The above is a suggestion. Tailor it to your needs.
<example module='xmpl'>
<file name="index.html">
@@ -133,19 +137,19 @@ angular.module('myModule', []).
// This is an example of config block.
// You can have as many of these as you want.
// You can only inject Providers (not instances)
// into the config blocks.
// into config blocks.
}).
run(function(injectables) { // instance-injector
// This is an example of a run block.
// You can have as many of these as you want.
// You can only inject instances (not Providers)
// into the run blocks
// into run blocks
});
```
## Configuration Blocks
There are some convenience methods on the module which are equivalent to the config block. For
There are some convenience methods on the module which are equivalent to the `config` block. For
example:
```js
@@ -166,8 +170,10 @@ angular.module('myModule', []).
});
```
The configuration blocks get applied in the order in which they are registered. The only exception
to it are constant definitions, which are placed at the beginning of all configuration blocks.
<div class="alert alert-info">
When bootstrapping, first Angular applies all constant definitions.
Then Angular applies configuration blocks in the order same order they were registered.
</div>
## Run Blocks
@@ -198,72 +204,73 @@ Beware that using `angular.module('myModule', [])` will create the module `myMod
existing module named `myModule`. Use `angular.module('myModule')` to retrieve an existing module.
```js
var myModule = angular.module('myModule', []);
// add some directives and services
myModule.service('myService', ...);
myModule.directive('myDirective', ...);
var myModule = angular.module('myModule', []);
// overwrites both myService and myDirective by creating a new module
var myModule = angular.module('myModule', []);
// add some directives and services
myModule.service('myService', ...);
myModule.directive('myDirective', ...);
// throws an error because myOtherModule has yet to be defined
var myModule = angular.module('myOtherModule');
// overwrites both myService and myDirective by creating a new module
var myModule = angular.module('myModule', []);
// throws an error because myOtherModule has yet to be defined
var myModule = angular.module('myOtherModule');
```
# Unit Testing
In its simplest form a unit test is a way of instantiating a subset of the application in test and
then applying a stimulus to it. It is important to realize that each module can only be loaded
once per injector. Typically an app has only one injector. But in tests, each test has its own
injector, which means that the modules are loaded multiple times per VM. Properly structured
modules can help with unit testing, as in this example:
A unit test is a way of instantiating a subset of an application to apply stimulus to it.
Small, structured modules help keep unit tests concise and focused.
<div class="did you know...">
Each module can only be loaded once per injector.
Usually an Angular app has only one injector and modules are only loaded once.
Each test has its own injector and modules are loaded multiple times.
</div>
In all of these examples we are going to assume this module definition:
```js
angular.module('greetMod', []).
angular.module('greetMod', []).
factory('alert', function($window) {
return function(text) {
$window.alert(text);
}
}).
factory('alert', function($window) {
return function(text) {
$window.alert(text);
}
}).
value('salutation', 'Hello').
value('salutation', 'Hello').
factory('greet', function(alert, salutation) {
return function(name) {
alert(salutation + ' ' + name + '!');
}
});
factory('greet', function(alert, salutation) {
return function(name) {
alert(salutation + ' ' + name + '!');
}
});
```
Let's write some tests:
Let's write some tests to show how to override configuration in tests.
```js
describe('myApp', function() {
// load the relevant application modules then load a special
// test module which overrides the $window with a mock version,
// so that calling window.alert() will not block the test
// runner with a real alert box. This is an example of overriding
// configuration information in tests.
// load application module (`greetMod`) then load a special
// test module which overrides `$window` with a mock version,
// so that calling `window.alert()` will not block the test
// runner with a real alert box.
beforeEach(module('greetMod', function($provide) {
$provide.value('$window', {
alert: jasmine.createSpy('alert')
});
}));
// The inject() will create the injector and inject the greet and
// $window into the tests. The test need not concern itself with
// wiring of the application, only with testing it.
// inject() will create the injector and inject the `greet` and
// `$window` into the tests.
it('should alert on $window', inject(function(greet, $window) {
greet('World');
expect($window.alert).toHaveBeenCalledWith('Hello World!');
}));
// this is another way of overriding configuration in the
// tests using an inline module and inject methods.
// tests using inline `module` and `inject` methods.
it('should alert using the alert service', function() {
var alertSpy = jasmine.createSpy('alert');
module(function($provide) {