mirror of
https://github.com/zhigang1992/angular.js.git
synced 2026-01-12 22:45:52 +08:00
305 lines
9.5 KiB
Plaintext
305 lines
9.5 KiB
Plaintext
@ngdoc overview
|
||
@name Modules
|
||
@sortOrder 320
|
||
@description
|
||
|
||
# What is a Module?
|
||
|
||
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 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
|
||
|
||
I'm in a hurry. How do I get a Hello World module working?
|
||
|
||
<example ng-app-included="true">
|
||
<file name="index.html">
|
||
<div ng-app="myApp">
|
||
<div>
|
||
{{ 'World' | greet }}
|
||
</div>
|
||
</div>
|
||
</file>
|
||
|
||
<file name="script.js">
|
||
// declare a module
|
||
var myAppModule = angular.module('myApp', []);
|
||
|
||
// configure the module.
|
||
// in this example we will create a greeting filter
|
||
myAppModule.filter('greet', function() {
|
||
return function(name) {
|
||
return 'Hello, ' + name + '!';
|
||
};
|
||
});
|
||
</file>
|
||
|
||
<file name="protractor.js" type="protractor">
|
||
it('should add Hello to the name', function() {
|
||
expect(element(by.binding("'World' | greet")).getText()).toEqual('Hello, World!');
|
||
});
|
||
</file>
|
||
</example>
|
||
|
||
Important things to notice:
|
||
|
||
* The {@link angular.Module Module} API
|
||
* The reference to `myApp` module in `<div 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
|
||
|
||
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 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.
|
||
|
||
We've also
|
||
[written a document](http://blog.angularjs.org/2014/02/an-angularjs-style-guide-and-best.html)
|
||
on how we organize large apps at Google.
|
||
|
||
The above is a suggestion. Tailor it to your needs.
|
||
|
||
<example module='xmpl'>
|
||
<file name="index.html">
|
||
<div ng-controller="XmplController">
|
||
{{ greeting }}
|
||
</div>
|
||
</file>
|
||
|
||
<file name="script.js">
|
||
angular.module('xmpl.service', [])
|
||
|
||
.value('greeter', {
|
||
salutation: 'Hello',
|
||
localize: function(localization) {
|
||
this.salutation = localization.salutation;
|
||
},
|
||
greet: function(name) {
|
||
return this.salutation + ' ' + name + '!';
|
||
}
|
||
})
|
||
|
||
.value('user', {
|
||
load: function(name) {
|
||
this.name = name;
|
||
}
|
||
});
|
||
|
||
angular.module('xmpl.directive', []);
|
||
|
||
angular.module('xmpl.filter', []);
|
||
|
||
angular.module('xmpl', ['xmpl.service', 'xmpl.directive', 'xmpl.filter'])
|
||
|
||
.run(function(greeter, user) {
|
||
// This is effectively part of the main method initialization code
|
||
greeter.localize({
|
||
salutation: 'Bonjour'
|
||
});
|
||
user.load('World');
|
||
})
|
||
|
||
.controller('XmplController', function($scope, greeter, user){
|
||
$scope.greeting = greeter.greet(user.name);
|
||
});
|
||
|
||
</file>
|
||
|
||
<file name="protractor.js" type="protractor">
|
||
it('should add Hello to the name', function() {
|
||
expect(element(by.binding("greeting")).getText()).toEqual('Bonjour World!');
|
||
});
|
||
</file>
|
||
|
||
</example>
|
||
|
||
|
||
|
||
# Module Loading & Dependencies
|
||
|
||
A module is a collection of configuration and run blocks which get applied to the application
|
||
during the bootstrap process. In its simplest form the module consist of a collection of two kinds
|
||
of blocks:
|
||
|
||
1. **Configuration blocks** - get executed during the provider registrations and configuration
|
||
phase. Only providers and constants can be injected into configuration blocks. This is to
|
||
prevent accidental instantiation of services before they have been fully configured.
|
||
2. **Run blocks** - get executed after the injector is created and are used to kickstart the
|
||
application. Only instances and constants can be injected into run blocks. This is to prevent
|
||
further system configuration during application run time.
|
||
|
||
```js
|
||
angular.module('myModule', []).
|
||
config(function(injectables) { // provider-injector
|
||
// 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 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 run blocks
|
||
});
|
||
```
|
||
|
||
## Configuration Blocks
|
||
|
||
There are some convenience methods on the module which are equivalent to the `config` block. For
|
||
example:
|
||
|
||
```js
|
||
angular.module('myModule', []).
|
||
value('a', 123).
|
||
factory('a', function() { return 123; }).
|
||
directive('directiveName', ...).
|
||
filter('filterName', ...);
|
||
|
||
// is same as
|
||
|
||
angular.module('myModule', []).
|
||
config(function($provide, $compileProvider, $filterProvider) {
|
||
$provide.value('a', 123);
|
||
$provide.factory('a', function() { return 123; });
|
||
$compileProvider.directive('directiveName', ...);
|
||
$filterProvider.register('filterName', ...);
|
||
});
|
||
```
|
||
|
||
<div class="alert alert-info">
|
||
When bootstrapping, first Angular applies all constant definitions.
|
||
Then Angular applies configuration blocks in the same order they were registered.
|
||
</div>
|
||
|
||
## Run Blocks
|
||
|
||
Run blocks are the closest thing in Angular to the main method. A run block is the code which
|
||
needs to run to kickstart the application. It is executed after all of the services have been
|
||
configured and the injector has been created. Run blocks typically contain code which is hard
|
||
to unit-test, and for this reason should be declared in isolated modules, so that they can be
|
||
ignored in the unit-tests.
|
||
|
||
## Dependencies
|
||
|
||
Modules can list other modules as their dependencies. Depending on a module implies that the required
|
||
module needs to be loaded before the requiring module is loaded. In other words the configuration
|
||
blocks of the required modules execute before the configuration blocks of the requiring module.
|
||
The same is true for the run blocks. Each module can only be loaded once, even if multiple other
|
||
modules require it.
|
||
|
||
## Asynchronous Loading
|
||
|
||
Modules are a way of managing $injector configuration, and have nothing to do with loading of
|
||
scripts into a VM. There are existing projects which deal with script loading, which may be used
|
||
with Angular. Because modules do nothing at load time they can be loaded into the VM in any order
|
||
and thus script loaders can take advantage of this property and parallelize the loading process.
|
||
|
||
## Creation versus Retrieval
|
||
|
||
Beware that using `angular.module('myModule', [])` will create the module `myModule` and overwrite any
|
||
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', ...);
|
||
|
||
// 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
|
||
|
||
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', []).
|
||
|
||
factory('alert', function($window) {
|
||
return function(text) {
|
||
$window.alert(text);
|
||
}
|
||
}).
|
||
|
||
value('salutation', 'Hello').
|
||
|
||
factory('greet', function(alert, salutation) {
|
||
return function(name) {
|
||
alert(salutation + ' ' + name + '!');
|
||
}
|
||
});
|
||
```
|
||
|
||
Let's write some tests to show how to override configuration in tests.
|
||
|
||
```js
|
||
describe('myApp', function() {
|
||
// 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')
|
||
});
|
||
}));
|
||
|
||
// 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 inline `module` and `inject` methods.
|
||
it('should alert using the alert service', function() {
|
||
var alertSpy = jasmine.createSpy('alert');
|
||
module(function($provide) {
|
||
$provide.value('alert', alertSpy);
|
||
});
|
||
inject(function(greet) {
|
||
greet('World');
|
||
expect(alertSpy).toHaveBeenCalledWith('Hello World!');
|
||
});
|
||
});
|
||
});
|
||
```
|