2012-02-23 09:53:14 -08:00
|
|
|
|
@ngdoc overview
|
2014-02-15 16:09:27 +00:00
|
|
|
|
@name Modules
|
2014-09-04 16:49:16 +01:00
|
|
|
|
@sortOrder 320
|
2012-02-23 09:53:14 -08:00
|
|
|
|
@description
|
|
|
|
|
|
|
2018-01-17 12:00:10 +01:00
|
|
|
|
# Modules
|
|
|
|
|
|
|
|
|
|
|
|
## What is a Module?
|
2012-02-23 09:53:14 -08:00
|
|
|
|
|
2014-03-03 12:51:56 -08:00
|
|
|
|
You can think of a module as a container for the different parts of your app – controllers,
|
|
|
|
|
|
services, filters, directives, etc.
|
|
|
|
|
|
|
2018-01-17 12:00:10 +01:00
|
|
|
|
## Why?
|
2014-03-03 12:51:56 -08:00
|
|
|
|
|
|
|
|
|
|
Most applications have a main method that instantiates and wires together the different parts of
|
|
|
|
|
|
the application.
|
|
|
|
|
|
|
2017-01-24 17:23:54 +00:00
|
|
|
|
AngularJS apps don't have a main method. Instead modules declaratively specify how an application
|
2012-10-14 09:57:18 -06:00
|
|
|
|
should be bootstrapped. There are several advantages to this approach:
|
2012-02-23 09:53:14 -08:00
|
|
|
|
|
2014-03-03 12:51:56 -08:00
|
|
|
|
* 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.
|
2012-02-23 09:53:14 -08:00
|
|
|
|
|
|
|
|
|
|
|
2018-01-17 12:00:10 +01:00
|
|
|
|
## The Basics
|
2012-02-23 09:53:14 -08:00
|
|
|
|
|
2014-03-03 12:51:56 -08:00
|
|
|
|
I'm in a hurry. How do I get a Hello World module working?
|
2012-02-23 09:53:14 -08:00
|
|
|
|
|
2016-07-27 18:30:35 +01:00
|
|
|
|
<example ng-app-included="true" name="module-hello-world">
|
2014-02-15 20:19:10 -05:00
|
|
|
|
<file name="index.html">
|
2014-08-19 11:26:53 -04:00
|
|
|
|
<div ng-app="myApp">
|
|
|
|
|
|
<div>
|
|
|
|
|
|
{{ 'World' | greet }}
|
|
|
|
|
|
</div>
|
2012-02-23 09:53:14 -08:00
|
|
|
|
</div>
|
2014-02-15 20:19:10 -05:00
|
|
|
|
</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>
|
2014-07-18 11:05:39 +01:00
|
|
|
|
|
|
|
|
|
|
<file name="protractor.js" type="protractor">
|
|
|
|
|
|
it('should add Hello to the name', function() {
|
2014-06-09 22:20:47 -07:00
|
|
|
|
expect(element(by.binding("'World' | greet")).getText()).toEqual('Hello, World!');
|
2014-07-18 11:05:39 +01:00
|
|
|
|
});
|
|
|
|
|
|
</file>
|
2014-02-15 20:19:10 -05:00
|
|
|
|
</example>
|
2012-02-23 09:53:14 -08:00
|
|
|
|
|
2014-03-03 12:51:56 -08:00
|
|
|
|
Important things to notice:
|
|
|
|
|
|
|
|
|
|
|
|
* The {@link angular.Module Module} API
|
2014-08-21 23:05:57 -04:00
|
|
|
|
* The reference to `myApp` module in `<div ng-app="myApp">`.
|
2014-03-03 12:51:56 -08:00
|
|
|
|
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.
|
2012-02-23 09:53:14 -08:00
|
|
|
|
|
|
|
|
|
|
|
2018-01-17 12:00:10 +01:00
|
|
|
|
## Recommended Setup
|
2012-02-23 09:53:14 -08:00
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
2014-03-03 12:51:56 -08:00
|
|
|
|
* 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
|
2012-02-23 09:53:14 -08:00
|
|
|
|
initialization code.
|
|
|
|
|
|
|
2016-07-10 11:46:36 +03:00
|
|
|
|
You can find a community [style guide](https://github.com/johnpapa/angular-styleguide) to help
|
|
|
|
|
|
yourself when application grows.
|
2012-02-23 09:53:14 -08:00
|
|
|
|
|
2014-03-03 12:51:56 -08:00
|
|
|
|
The above is a suggestion. Tailor it to your needs.
|
2012-02-23 09:53:14 -08:00
|
|
|
|
|
2016-07-27 18:30:35 +01:00
|
|
|
|
<example module='xmpl' name="module-suggested-layout">
|
2014-02-15 20:19:10 -05:00
|
|
|
|
<file name="index.html">
|
|
|
|
|
|
<div ng-controller="XmplController">
|
2014-07-18 11:05:39 +01:00
|
|
|
|
{{ greeting }}
|
2014-02-15 20:19:10 -05:00
|
|
|
|
</div>
|
|
|
|
|
|
</file>
|
|
|
|
|
|
|
|
|
|
|
|
<file name="script.js">
|
2014-07-18 11:05:24 +01:00
|
|
|
|
angular.module('xmpl.service', [])
|
|
|
|
|
|
|
|
|
|
|
|
.value('greeter', {
|
2014-02-15 20:19:10 -05:00
|
|
|
|
salutation: 'Hello',
|
|
|
|
|
|
localize: function(localization) {
|
|
|
|
|
|
this.salutation = localization.salutation;
|
|
|
|
|
|
},
|
|
|
|
|
|
greet: function(name) {
|
|
|
|
|
|
return this.salutation + ' ' + name + '!';
|
|
|
|
|
|
}
|
2014-07-18 11:05:24 +01:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
.value('user', {
|
2014-02-15 20:19:10 -05:00
|
|
|
|
load: function(name) {
|
|
|
|
|
|
this.name = name;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2012-02-23 09:53:14 -08:00
|
|
|
|
|
2014-02-15 20:19:10 -05:00
|
|
|
|
angular.module('xmpl.directive', []);
|
2012-02-23 09:53:14 -08:00
|
|
|
|
|
2014-02-15 20:19:10 -05:00
|
|
|
|
angular.module('xmpl.filter', []);
|
2014-07-18 11:05:24 +01:00
|
|
|
|
|
|
|
|
|
|
angular.module('xmpl', ['xmpl.service', 'xmpl.directive', 'xmpl.filter'])
|
|
|
|
|
|
|
|
|
|
|
|
.run(function(greeter, user) {
|
2014-02-15 20:19:10 -05:00
|
|
|
|
// This is effectively part of the main method initialization code
|
|
|
|
|
|
greeter.localize({
|
|
|
|
|
|
salutation: 'Bonjour'
|
|
|
|
|
|
});
|
|
|
|
|
|
user.load('World');
|
2014-07-18 11:05:24 +01:00
|
|
|
|
})
|
|
|
|
|
|
|
2016-07-20 15:45:04 +02:00
|
|
|
|
.controller('XmplController', function($scope, greeter, user) {
|
2014-07-18 01:26:49 +02:00
|
|
|
|
$scope.greeting = greeter.greet(user.name);
|
2014-02-15 20:19:10 -05:00
|
|
|
|
});
|
2012-02-23 09:53:14 -08:00
|
|
|
|
|
2014-02-15 20:19:10 -05:00
|
|
|
|
</file>
|
2014-07-18 11:05:39 +01:00
|
|
|
|
|
|
|
|
|
|
<file name="protractor.js" type="protractor">
|
|
|
|
|
|
it('should add Hello to the name', function() {
|
2014-06-09 22:20:47 -07:00
|
|
|
|
expect(element(by.binding("greeting")).getText()).toEqual('Bonjour World!');
|
2014-07-18 11:05:39 +01:00
|
|
|
|
});
|
|
|
|
|
|
</file>
|
|
|
|
|
|
|
2014-02-15 20:19:10 -05:00
|
|
|
|
</example>
|
2012-02-23 09:53:14 -08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-01-17 12:00:10 +01:00
|
|
|
|
## Module Loading
|
2012-02-23 09:53:14 -08:00
|
|
|
|
|
2018-01-17 12:00:10 +01:00
|
|
|
|
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.
|
2012-02-23 09:53:14 -08:00
|
|
|
|
|
2018-01-17 12:00:10 +01:00
|
|
|
|
The {@link angular.Module module API} describes all the available methods and how they can be used.
|
2012-02-23 09:53:14 -08:00
|
|
|
|
|
2018-01-17 12:00:10 +01:00
|
|
|
|
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}.
|
2012-02-23 09:53:14 -08:00
|
|
|
|
|
2018-01-17 12:00:10 +01:00
|
|
|
|
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:
|
2012-02-23 09:53:14 -08:00
|
|
|
|
|
|
|
|
|
|
|
2014-02-06 14:02:18 +00:00
|
|
|
|
```js
|
2012-02-23 09:53:14 -08:00
|
|
|
|
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) {
|
2013-02-06 06:14:12 +04:00
|
|
|
|
$provide.value('a', 123);
|
|
|
|
|
|
$provide.factory('a', function() { return 123; });
|
|
|
|
|
|
$compileProvider.directive('directiveName', ...);
|
2012-02-23 09:53:14 -08:00
|
|
|
|
$filterProvider.register('filterName', ...);
|
|
|
|
|
|
});
|
2014-02-06 14:02:18 +00:00
|
|
|
|
```
|
2012-02-23 09:53:14 -08:00
|
|
|
|
|
2018-01-17 12:00:10 +01:00
|
|
|
|
### Run Blocks
|
2012-02-23 09:53:14 -08:00
|
|
|
|
|
2017-01-24 17:23:54 +00:00
|
|
|
|
Run blocks are the closest thing in AngularJS to the main method. A run block is the code which
|
2014-12-27 03:22:21 +00:00
|
|
|
|
needs to run to kickstart the application. It is executed after all of the services have been
|
2012-03-07 16:17:23 -08:00
|
|
|
|
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
|
2012-02-23 09:53:14 -08:00
|
|
|
|
ignored in the unit-tests.
|
|
|
|
|
|
|
2018-01-17 12:00:10 +01:00
|
|
|
|
### Asynchronous Loading
|
2012-02-23 09:53:14 -08:00
|
|
|
|
|
|
|
|
|
|
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
|
2017-01-24 17:23:54 +00:00
|
|
|
|
with AngularJS. Because modules do nothing at load time they can be loaded into the VM in any order
|
2012-03-23 16:57:24 -07:00
|
|
|
|
and thus script loaders can take advantage of this property and parallelize the loading process.
|
2012-02-23 09:53:14 -08:00
|
|
|
|
|
2018-01-17 12:00:10 +01:00
|
|
|
|
### Creation versus Retrieval
|
2013-08-11 16:03:17 -07:00
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
2014-02-06 14:02:18 +00:00
|
|
|
|
```js
|
2014-03-03 12:51:56 -08:00
|
|
|
|
var myModule = angular.module('myModule', []);
|
|
|
|
|
|
|
|
|
|
|
|
// add some directives and services
|
|
|
|
|
|
myModule.service('myService', ...);
|
|
|
|
|
|
myModule.directive('myDirective', ...);
|
2013-08-11 16:03:17 -07:00
|
|
|
|
|
2014-03-03 12:51:56 -08:00
|
|
|
|
// overwrites both myService and myDirective by creating a new module
|
|
|
|
|
|
var myModule = angular.module('myModule', []);
|
2013-08-11 16:03:17 -07:00
|
|
|
|
|
2014-03-03 12:51:56 -08:00
|
|
|
|
// throws an error because myOtherModule has yet to be defined
|
|
|
|
|
|
var myModule = angular.module('myOtherModule');
|
2014-02-06 14:02:18 +00:00
|
|
|
|
```
|
2012-02-23 09:53:14 -08:00
|
|
|
|
|
2018-01-17 12:00:10 +01:00
|
|
|
|
## Unit Testing
|
2012-02-23 09:53:14 -08:00
|
|
|
|
|
2014-03-03 12:51:56 -08:00
|
|
|
|
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.
|
2017-01-24 17:23:54 +00:00
|
|
|
|
Usually an AngularJS app has only one injector and modules are only loaded once.
|
2014-03-03 12:51:56 -08:00
|
|
|
|
Each test has its own injector and modules are loaded multiple times.
|
|
|
|
|
|
</div>
|
2012-02-23 09:53:14 -08:00
|
|
|
|
|
|
|
|
|
|
In all of these examples we are going to assume this module definition:
|
2014-02-06 14:02:18 +00:00
|
|
|
|
|
|
|
|
|
|
```js
|
2014-03-03 12:51:56 -08:00
|
|
|
|
angular.module('greetMod', []).
|
2012-02-23 09:53:14 -08:00
|
|
|
|
|
2014-03-03 12:51:56 -08:00
|
|
|
|
factory('alert', function($window) {
|
|
|
|
|
|
return function(text) {
|
|
|
|
|
|
$window.alert(text);
|
|
|
|
|
|
}
|
|
|
|
|
|
}).
|
2012-02-23 09:53:14 -08:00
|
|
|
|
|
2014-03-03 12:51:56 -08:00
|
|
|
|
value('salutation', 'Hello').
|
2012-02-23 09:53:14 -08:00
|
|
|
|
|
2014-03-03 12:51:56 -08:00
|
|
|
|
factory('greet', function(alert, salutation) {
|
|
|
|
|
|
return function(name) {
|
|
|
|
|
|
alert(salutation + ' ' + name + '!');
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2014-02-06 14:02:18 +00:00
|
|
|
|
```
|
2012-02-23 09:53:14 -08:00
|
|
|
|
|
2014-03-03 12:51:56 -08:00
|
|
|
|
Let's write some tests to show how to override configuration in tests.
|
2014-02-06 14:02:18 +00:00
|
|
|
|
|
|
|
|
|
|
```js
|
2012-02-23 09:53:14 -08:00
|
|
|
|
describe('myApp', function() {
|
2014-03-03 12:51:56 -08:00
|
|
|
|
// 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.
|
2012-02-23 09:53:14 -08:00
|
|
|
|
beforeEach(module('greetMod', function($provide) {
|
2012-03-07 16:17:23 -08:00
|
|
|
|
$provide.value('$window', {
|
|
|
|
|
|
alert: jasmine.createSpy('alert')
|
2012-02-23 09:53:14 -08:00
|
|
|
|
});
|
2012-03-22 20:38:49 +01:00
|
|
|
|
}));
|
2012-02-23 09:53:14 -08:00
|
|
|
|
|
2014-03-03 12:51:56 -08:00
|
|
|
|
// inject() will create the injector and inject the `greet` and
|
|
|
|
|
|
// `$window` into the tests.
|
2012-02-23 09:53:14 -08:00
|
|
|
|
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
|
2014-03-03 12:51:56 -08:00
|
|
|
|
// tests using inline `module` and `inject` methods.
|
2012-02-23 09:53:14 -08:00
|
|
|
|
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');
|
2012-03-22 20:38:49 +01:00
|
|
|
|
expect(alertSpy).toHaveBeenCalledWith('Hello World!');
|
2012-02-23 09:53:14 -08:00
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
2014-02-06 14:02:18 +00:00
|
|
|
|
```
|