a7e5e83240
Closes #16363 Closes #16395
300 lines
9.4 KiB
Plaintext
300 lines
9.4 KiB
Plaintext
@ngdoc overview
|
||
@name Modules
|
||
@sortOrder 320
|
||
@description
|
||
|
||
# Modules
|
||
|
||
## 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.
|
||
|
||
AngularJS 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" name="module-hello-world">
|
||
<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.
|
||
|
||
You can find a community [style guide](https://github.com/johnpapa/angular-styleguide) to help
|
||
yourself when application grows.
|
||
|
||
The above is a suggestion. Tailor it to your needs.
|
||
|
||
<example module='xmpl' name="module-suggested-layout">
|
||
<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
|
||
|
||
A {@link angular.Module module} is a collection of providers, services, directives etc.,
|
||
and optionally config and run blocks which get applied to the application during the
|
||
bootstrap process.
|
||
|
||
The {@link angular.Module module API} describes all the available methods and how they can be used.
|
||
|
||
See {@link guide/di#using-dependency-injection Using Dependency Injection} to find out which
|
||
dependencies can be injected in each method.
|
||
|
||
### Dependencies and Order of execution
|
||
|
||
Modules can list other modules as their dependencies. Depending on a module implies that the required
|
||
module will be loaded before the requiring module is loaded.
|
||
|
||
In a single module the order of execution is as follows:
|
||
|
||
1. {@link angular.Module#provider provider} functions are executed, so they and the services they
|
||
define can be made available to the {@link auto.$injector $injector}.
|
||
|
||
2. After that, the configuration blocks ({@link angular.Module#config config} functions) are executed.
|
||
This means the configuration blocks of the required modules execute before the configuration blocks
|
||
of any requiring module.
|
||
|
||
This continues until all module dependencies has been resolved.
|
||
|
||
Then, the {@link angular.Module#run run} blocks that have been collected from each module are
|
||
executed in order of requirement.
|
||
|
||
Note: each module is only loaded once, even if multiple other modules require it.
|
||
Note: the factory function for "values" and "services" is called lazily when the value/service is
|
||
injected for the first time.
|
||
|
||
### Registration in the config block
|
||
|
||
While it is recommended to register injectables directly with the {@link angular.Module module API},
|
||
it is also possible to register services, directives etc. by injecting
|
||
{@link $provide $provide} or the individual service providers into the config function:
|
||
|
||
|
||
```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', ...);
|
||
});
|
||
```
|
||
|
||
### Run Blocks
|
||
|
||
Run blocks are the closest thing in AngularJS 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.
|
||
|
||
### 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 AngularJS. 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 AngularJS 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!');
|
||
});
|
||
});
|
||
});
|
||
```
|