263 lines
12 KiB
HTML
Executable file
263 lines
12 KiB
HTML
Executable file
<a href="http://github.com/angular/angular.js/edit/master/docs/content/guide/module.ngdoc" class="improve-docs btn btn-primary"><i class="icon-edit"> </i> Improve this doc</a><h1><code ng:non-bindable=""></code>
|
|
<div><span class="hint"></span>
|
|
</div>
|
|
</h1>
|
|
<div><div class="developer-guide-page developer-guide-modules-page"><h2>What is a Module?</h1>
|
|
<p>Most applications have a main method which instantiates, wires, and bootstraps 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:</p>
|
|
<ul>
|
|
<li>The process is more declarative which is easier to understand</li>
|
|
<li>In unit-testing there is no need to load all modules, which may aid in writing unit-tests.</li>
|
|
<li>Additional modules can be loaded in scenario tests, which can override some of the
|
|
configuration and help end-to-end test the application</li>
|
|
<li>Third party code can be packaged as reusable modules.</li>
|
|
<li>The modules can be loaded in any/parallel order (due to delayed nature of module execution).</li>
|
|
</ul>
|
|
<h1>The Basics</h1>
|
|
<p>Ok, I'm in a hurry. How do I get a Hello World module working?</p>
|
|
<p>Important things to notice:</p>
|
|
<ul>
|
|
<li><a href="api/angular.Module"><code>Module</code></a> API</li>
|
|
<li>Notice the reference to the <code>myApp</code> module in the <code><html ng-app="myApp"></code>, it is what
|
|
bootstraps the app using your module.</li>
|
|
</ul>
|
|
<h3>Source</h2>
|
|
<div source-edit="myApp" source-edit-deps="angular.js script.js" source-edit-html="index.html-235" source-edit-css="" source-edit-js="script.js-234" source-edit-json="" source-edit-unit="" source-edit-scenario=""></div>
|
|
<div class="tabbable"><div class="tab-pane" title="index.html">
|
|
<pre class="prettyprint linenums" ng-set-text="index.html-235" ng-html-wrap="myApp angular.js script.js"></pre>
|
|
<script type="text/ng-template" id="index.html-235">
|
|
|
|
<div>
|
|
{{ 'World' | greet }}
|
|
</div>
|
|
</script>
|
|
</div>
|
|
<div class="tab-pane" title="script.js">
|
|
<pre class="prettyprint linenums" ng-set-text="script.js-234"></pre>
|
|
<script type="text/ng-template" id="script.js-234">
|
|
// 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 + '!';
|
|
};
|
|
});
|
|
|
|
</script>
|
|
</div>
|
|
</div><h2>Demo</h2>
|
|
<div class="well doc-example-live animate-container" ng-embed-app="myApp" ng-set-html="index.html-235" ng-eval-javascript="script.js-234"></div>
|
|
<h1>Recommended Setup</h1>
|
|
<p>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:</p>
|
|
<ul>
|
|
<li>A service module, for service declaration</li>
|
|
<li>A directive module, for directive declaration</li>
|
|
<li>A filter module, for filter declaration</li>
|
|
<li>And an application level module which depends on the above modules, and which has
|
|
initialization code.</li>
|
|
</ul>
|
|
<p>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.</p>
|
|
<p>The above is only a suggestion, so feel free to tailor it to your needs.</p>
|
|
<h2>Source</h2>
|
|
<div source-edit="xmpl" source-edit-deps="angular.js script.js" source-edit-html="index.html-237" source-edit-css="" source-edit-js="script.js-236" source-edit-json="" source-edit-unit="" source-edit-scenario=""></div>
|
|
<div class="tabbable"><div class="tab-pane" title="index.html">
|
|
<pre class="prettyprint linenums" ng-set-text="index.html-237" ng-html-wrap="xmpl angular.js script.js"></pre>
|
|
<script type="text/ng-template" id="index.html-237">
|
|
|
|
<div ng-controller="XmplController">
|
|
{{ greeting }}!
|
|
</div>
|
|
</script>
|
|
</div>
|
|
<div class="tab-pane" title="script.js">
|
|
<pre class="prettyprint linenums" ng-set-text="script.js-236"></pre>
|
|
<script type="text/ng-template" id="script.js-236">
|
|
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');
|
|
})
|
|
|
|
|
|
// A Controller for your app
|
|
var XmplController = function($scope, greeter, user) {
|
|
$scope.greeting = greeter.greet(user.name);
|
|
}
|
|
</script>
|
|
</div>
|
|
</div><h2>Demo</h2>
|
|
<div class="well doc-example-live animate-container" ng-embed-app="xmpl" ng-set-html="index.html-237" ng-eval-javascript="script.js-236"></div>
|
|
<h1>Module Loading & Dependencies</h1>
|
|
<p>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 collection of two kinds
|
|
of blocks:</p>
|
|
<ol>
|
|
<li><strong>Configuration blocks</strong> - 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.</li>
|
|
<li><strong>Run blocks</strong> - 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.</li>
|
|
</ol>
|
|
<pre class="prettyprint linenums">
|
|
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 the 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
|
|
});
|
|
</pre>
|
|
<h2>Configuration Blocks</h2>
|
|
<p>There are some convenience methods on the module which are equivalent to the config block. For
|
|
example:</p>
|
|
<pre class="prettyprint linenums">
|
|
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', ...);
|
|
});
|
|
</pre>
|
|
<p>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.</p>
|
|
<h2>Run Blocks</h2>
|
|
<p>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 service 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.</p>
|
|
<h2>Dependencies</h2>
|
|
<p>Modules can list other modules as their dependencies. Depending on a module implies that 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.</p>
|
|
<h2>Asynchronous Loading</h2>
|
|
<p>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.</p>
|
|
<h2>Creation versus Retrieval</h3>
|
|
<p>Beware that using <code>angular.module('myModule', [])</code> will create the module <code>myModule</code> and overwrite any
|
|
existing module named <code>myModule</code>. Use <code>angular.module('myModule')</code> to retrieve an existing module.</p>
|
|
<pre class="prettyprint linenums">
|
|
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');
|
|
</pre>
|
|
<h1>Unit Testing</h2>
|
|
<p>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:</p>
|
|
<p>In all of these examples we are going to assume this module definition:
|
|
<pre class="prettyprint linenums">
|
|
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 + '!');
|
|
}
|
|
});
|
|
</pre>
|
|
<p>Let's write some tests:
|
|
<pre class="prettyprint linenums">
|
|
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.
|
|
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.
|
|
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.
|
|
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!');
|
|
});
|
|
});
|
|
});
|
|
</pre>
|
|
</div></div>
|