モジュール
モジュールとは何なのか?
多くのアプリケーションは、インスタンス化、各種接続、アプリケーションの起動を行う主(main)メソッドを持ちます。 Angularアプリケーションは、そういったmainメソッドを持ちません。 その代わり、モジュールにアプリケーションがどのように立ち上がるべきかの定義を指定します。 これには、下記のメリットがあります。
- プロセスがより宣言的で、理解しやすくなります。(翻訳に自信なし)
- ユニットテストで、全てのモジュールを読み込む必要が無く、ユニットテストが比較的書きやすくなります。
- 追加モジュールは、シナリオテストの読み込みが可能で、 設定のいくつかを上書きし、アプリケーションのエンドツーエンドテストの手助けしてくれます。
- サードパーティのコードを再利用可能なパッケージにすることが可能です。
- モジュールを任意/並列で順に読み込むことが可能です。 (モジュール実行の遅延性質のため)
基本
それでは、「Hello World」モジュールを動かしてみましょう。
注意すべき重要なことは下記のとおりです。
- Module APIを参照し、内容を把握しておいてください。
-
<html ng-app="myApp">
が、Angularに参照されることを覚えておいてください。 ここで、アプリケーション起動時に使用されるモジュールが指定されることになります。
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://code.angularjs.org/1.0.8/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body>
<div><!-- 挨拶フィルターの適用 -->
{{ 'World' | greet }}
</div>
</body>
</html>
// モジュールの宣言
var myAppModule = angular.module('myApp', []);
// モジュールの設定
// この例では、挨拶フィルターを作成します。
myAppModule.filter('greet', function() {
return function(name) {
return 'Hello, ' + name + '!';
};
});
<!doctype html> <html ng-app="myApp"> <head> <script src="http://code.angularjs.org/1.0.8/angular.min.js"></script> <script>// モジュールの宣言 var myAppModule = angular.module('myApp', []); // モジュールの設定 // この例では、挨拶フィルターを作成します。 myAppModule.filter('greet', function() { return function(name) { return 'Hello, ' + name + '!'; }; }); </script> </head> <body> <div><!-- 挨拶フィルターの適用 --> {{ 'World' | greet }} </div> </body> </html>
推奨セットアップ
上記のサンプルはシンプルではありますが、巨大なアプリケーションには対応できません。 代わりに、下記のようにアプリケーションを複数のモジュールに分割することをお勧めします。
-
サービス宣言のための、
service
モジュール -
ディレクティブ宣言のための、
directive
モジュール -
フィルター宣言のための、
filter
モジュール - そして、上記のモジュールに依存し、初期化コードを持つプリケーション層のモジュール
分割するのは、テストを困難にする初期化コードを無視したいというニーズがよくあるためです。 分割したモジュールにそれを配置することで、テストでの無視が容易になります。 また、テストに関連するモジュールのみを読み込むことで、そのテストに対してより集中することも可能になります。
上記は提案に過ぎないので、必要に応じて好きなように扱ってみてください。
<!doctype html>
<html ng-app="xmpl">
<head>
<script src="http://code.angularjs.org/1.0.8/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body>
<div ng-controller="XmplController">
{{ greeting }}!
</div>
</body>
</html>
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) {
// 主メソッドの初期化コードの一部となります。
greeter.localize({
salutation: 'Bonjour'
});
user.load('World');
})
// あなたのアプリケーションのコントローラーです
var XmplController = function($scope, greeter, user) {
$scope.greeting = greeter.greet(user.name);
}
<!doctype html> <html ng-app="xmpl"> <head> <script src="http://code.angularjs.org/1.0.8/angular.min.js"></script> <script>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) { // 主メソッドの初期化コードの一部となります。 greeter.localize({ salutation: 'Bonjour' }); user.load('World'); }) // あなたのアプリケーションのコントローラーです var XmplController = function($scope, greeter, user) { $scope.greeting = greeter.greet(user.name); } </script> </head> <body> <div ng-controller="XmplController"> {{ greeting }}! </div> </body> </html>
モジュールの読み込みと依存
モジュールは、起動プロセス中にアプリケーションに対して適用される設定と実行ブロックの集合です。 最も単純な形式のモジュールは、2種類のブロックから構成されているものです。
- 構成(config)ブロック - 構成ブロックは、providerの登録と設定の段階で実行されます。 providerと定数だけが、設定ブロックに注入することが可能です。 これは、設定が完全に完了する前にサービスのインスタン化が偶発的に行われるのを防ぐためです。
- 実行(run)ブロック - 実行ブロックは、インジェクターが作られアプリケーションのスタートに使用された後に実行されます。 インスタンスと定数だけが、実行ブロックに注入可能です。 これは、アプリケーション実行中にシステムの設定をされることを防ぐためです。
angular.module('myModule', []).
config(function(injectables) { // providerインジェクター
// これは構成ブロックの例です。
// 必要なだけ、これらを持つことが可能です。
// provider(インスタンスでは無い)のみを構成ブロックに注入可能です。
}).
run(function(injectables) { // インスタンスインジェクター
// これは実行ブロックの例です。
// 必要なだけ、これらを持つことが可能です。
// インスタンス(providerでは無い)のみを実行ブロックに注入可能です。
});
構成(config)ブロック
モジュールにはconfigブロックと同等の便利なメソッドが存在します。 例えば、
angular.module('myModule', []).
value('a', 123).
factory('a', function() { return 123; }).
directive('directiveName', ...).
filter('filterName', ...);
// 上記と下記は同等
angular.module('myModule', []).
config(function($provide, $compileProvider, $filterProvider) {
$provide.value('a', 123);
$provide.factory('a', function() { return 123; });
$compileProvider.directive('directiveName', ...);
$filterProvider.register('filterName', ...);
});
構成(config)ブロックは、登録された順序で適用され始めます。 1つだけ例外があり、定数の定義は、全ての構成ブロックの先頭に配置されます。
実行(run)ブロック
実行ブロックは、Angularとしてのmainメソッドの概念に最も近いものです。 実行ブロックのコードで、アプリケーション起動させる必要があります。 全てのserviceが構成され、全てのインジェクターが作成された後に実行されます。 実行ブロックには、ユニットテストがしにくいコードが含まれることがあるので、 それらのコードは、ユニットテストで無視出来るようにするため、関連するモジュールに定義すべきです。
依存関係
モジュールは、他のモジュールをリスト化することで依存関係を示すことが可能です。 モジュールに依存関係がある場合、必要とされるモジュールが、必要とするモジュールより前に、 読み込まれていなければいけないを意味します。 言い方を変えると、必要とされるモジュールの構成(config)ブロックは、 必要とするモジュールの構成(config0ブロックより先に実行されるべきという事になります。 同じ事が実行(run)ブロックにも当てはまります。 1つのモジュールが、複数のモジュールから必要とされている場合でも、1度だけ読みこめば問題ありません。
非同期読み込み
モジュールは$injector構成を管理する方法であり、 VM(JavaScript実行環境)内へのスクリプト読み込みとは無関係です。 スクリプトの読み込みを扱う既存のプロジェクトがあるとして、 そのスクリプトにAngularが使用されるかもしれません。 モジュールは読み込み時には何も行わないため、任意の順番でVM(JavaScript実行環境)に読み込むことが可能です。 そのため、スクリプトローダーはこのプロパティを使って、ロードプロセスを並列化することが出来ます。
作成 vs 修正
angular.module('myModule', [])
として、'myModule'モジュールを作る場合、
既存の'myModule'という名前のモジュールを上書きしてしまうことに注意してください。
既存のモジュールを取得するには、angular.module('myModule')
を使用してください。
//モジュールを定義
var myModule = angular.module('myModule', []);
// ディレクティブとサービスを追加
myModule.service('myService', ...);
myModule.directive('myDirective', ...);
// 新しいモジュールの作成によって、
// myServiceとmyDirectiveの両方は上書きされます。
var myModule = angular.module('myModule', []);
// myOtherModuleはまだ未定義のため、エラーがスローされます。
var myModule = angular.module('myOtherModule');
ユニットテスト
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:
In all of these examples we are going to assume this module definition:
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:
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!');
});
});
});
© 2017 Google
Licensed under the Creative Commons Attribution License 3.0.
このページは、ページトップのリンク先のAngularJS公式ドキュメント内のページを翻訳した内容を基に構成されています。 下記の項目を確認し、必要に応じて公式のドキュメントをご確認ください。 もし、誤訳などの間違いを見つけましたら、 @tomofまで教えていただければ幸いです。
- AngularJSの更新頻度が高いため、元のコンテンツと比べてドキュメントの情報が古くなっている可能性があります。
- "訳注:"などの断わりを入れた上で、日本人向けの情報やより分かり易くするための追記を行っている事があります。